Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Async and await the Future

There are three keywords in an asynchronous call, async, await, and Future, where async and await need to be used together. Asynchronous operations can be performed in Dart with async and await. Async means to start an asynchronous operation and can return a Future result. If no value is returned, a Future with a null value is returned by default.

Await operations do not affect the execution of subsequent code outside the method; Only subsequent code for async methods is blocked

Example 1

_testAsyncKeyword() {print("test starts: ${datetime.now ()}"); _testString().then((value) => print(value)); ${datetime.now ()}"); } Future<String> _testString() async { Future f = Future.delayed(Duration(milliseconds: 300), () {return "I'm testing string ===1"; }); String result = await f; Print (" I'm testing string ===2"); return result; } // flutter: test function starts: // flutter: test function ends: // FLUTTER: I am a test string ===2 // FLUTTER: I am a test string ===1Copy the code

In the code example, execution to the _testString() method synchronously goes inside the method to execute, and when execution reaches await the async execution stops and the outside code continues. So await operations will not affect subsequent code execution (“test is finished “will print internally before _testString()). When await is returned, execution continues from await position (so “I am test string ===2″ is printed, then Future is returned and” I am test string ===1″ is printed by print).

Example 2

_testAsyncKeyword() async {print("test function starts: ${datetime.now ()}"); print(await _testString()); ${datetime.now ()}"); } Future<String> _testString() async { Future f = Future.delayed(Duration(milliseconds: 300), () {return "I'm testing string ===1"; }); String result = await f; Print (" I'm testing string ===2"); return result; } // flutter: test function starts: // flutter: I am a test string ===2 // FLUTTER: TEST function ends:Copy the code

In the code example, _testAsyncKeyword() itself has an await operation inside and stops execution inside _testAsyncKeyword() async when an await operation is executed. Wait for the _testString() result to return and continue execution.

_testString() also has an await operation inside it, and will stop internal execution of _testString() async when an await operation is performed, wait 300 milliseconds, and print string 2 after the Future has a result

_testAsyncKeyword() continues to print string 1 and end

Example 3

_testAsyncKeyword() {print("test starts: ${datetime.now ()}"); firstString().then((value) => print(value)); secondString().then((value) => print(value)); thirdString().then((value) => print(value)); ${datetime.now ()}"); } _testKeyword2() async{print("test function starts: ${datetime.now ()}"); print(await firstString()); print(await secondString()); print(await thirdString()); ${datetime.now ()}"); } Future<String> firstString() {return future.delayed (Duration(milliseconds: 300), () {return "I'm a String "; }); } Future<String> secondString() {return future.delayed (Duration(milliseconds: 200), () {return "I'm two strings "; }); } Future<String> thirdString() {return future.delayed (Duration(milliseconds: 100), () {return "I'm three strings "; }); } //_testAsyncKeyword() prints: //flutter: test function starts: //flutter: test function ends: // FLUTTER: I am three strings //flutter: I am two strings // FLUTTER: I am a string //_testKeyword2() prints: // the function flutter: test starts: //flutter: I am a string //flutter: I am two strings // I am three strings // the function flutter: test ends:Copy the code

From the above three examples, we can see the difference and connection between await async and then.

Principle of async and await

Async and await operations are **” pseudo-asynchronous “**, why is this? If we want to get the answer to this question, we first need to understand the principle of async and await and the concept of coroutines, because async and await are essentially a syntactic sugar of coroutines. A coroutine, also known as a coroutine, is a unit smaller than a thread. In terms of unit size, it can basically be understood as process -> thread -> coroutine.

Task scheduling

Before you can understand coroutines, you need to understand the concepts of concurrency and parallelism

  • Concurrency: The system manages multiple I/O switches and assigns them to the CPU.
  • Parallelism: Refers to multi-core cpus performing multiple tasks at the same time.

Concurrent implementation is accomplished by non-blocking operations + event notification, also known as “interrupts”. There are two operations. One is that the CPU performs an OPERATION on the I/O and sends an interrupt to tell the I/O that the operation is complete. The other is when the IO initiates an interrupt to tell the CPU that it is ready to do something.

Thread: Essentially depends on interrupt for scheduling. There is also a kind of thread called “blocking interrupt”, which is to block the thread during THE IO operation and wait for the completion of the execution. A large number of concurrent operations can be carried out through single-thread concurrency. However, thread consumption is high, not suitable for the processing of large number of concurrent operations, and a single thread can only use a single CPU. With the advent of multi-core cpus, single threads can no longer take advantage of multi-core cpus, so the concept of thread pools was introduced to manage a large number of threads. When we need to perform more than one task at a time, we use multi-threading.

Dart single-threaded execution model: Input single-absorbed execution mechanism, mainly through the message loop mechanism for task scheduling and processing.

Coroutines coroutine

When a thread is waiting for I/OS, it blocks the current thread and switches to another thread. In this way, other threads can continue executing while the current thread is waiting for I/OS. When the system has fewer threads, there is no problem, but when the number of threads is very large, there is a problem. One is that system threads take up a lot of memory space, and the other is that too many thread switches take up a lot of system time. Coroutines run on top of the thread, and when one coroutine is finished, you can choose to give up and let another run on top of the current thread. Coroutines do not increase the number of threads, but run multiple coroutines on the basis of threads through time-sharing multiplexing, and the switching of coroutines is completed in user state, the cost of switching is much lower than the cost of thread from user state to kernel state. Coroutines are divided into wireless coroutines and wired coroutines.

  • The wireless coroutine places the current variable in the heap when it leaves the current calling location, and continues to fetch variables from the heap when it returns to the current location. Therefore, variables are allocated directly to the heap when the current function is executed, and async and await are one of the wireless coroutines.
  • Wired coroutines keep variables on the stack and continue to take calls off the stack when they return to the point where the pointer points.

Principle of async and await

The reason why async/await is pseudo-asynchrony is that it does not start a new thread and does not execute concurrently in the execution process. Instead, it is realized by task scheduling on a single thread (coroutine without concurrent execution function) : when the code is executed to async, it means that it enters a coroutine and synchronously executes the code block of async. An async block of code is essentially a function and has its own context. When an await is performed, it means that a task needs to wait, and the CPU schedules the execution of other IO, i.e., subsequent code or other coroutine code. After a period of time the CPU polls to see if a coroutine has finished processing, returns a result that can be continued, and, if it can be continued, continues execution along the location where the pointer pointed to the last time it left, i.e. the location of the await flag.

Since no new thread is started and only I/O interrupts are performed to change CPU scheduling, asynchronous operations such as network requests can be performed with async and await. However, if a large number of time-consuming synchronous operations are performed, new threads should be created using ISOLATE to execute them.