Inner Xuan, a front engineer at Alibaba, gives a detailed explanation of asynchronous programming in JavaScript. The characteristic of JavaScript is single thread. This paper first understands the principle of single thread asynchronization, and then focuses on the analysis of JavaScript asynchronous solutions, detailing the characteristics and application principle of Callback, Promise, Generator, Async/Await.

Dozens of Ali cloud products limited time discount, quickly click here, get the coupons to start the cloud practice!

Live video highlights

Here are the highlights:

Single threaded asynchrony

One of the hallpieces of the JavaScript language is single-threaded, where only certain code can be executed at any given time and block other code, meaning that only one thing can be done at a time.

The most common asynchronous operations are network requests, which have a response time and need to deal with other things before the response results come back. IO operations, such as reading a file, do other things along the way; Timing function.

Asynchronism is accomplished by two or more resident threads of the browser, such as asynchronous request: THE JS execution thread initiates an asynchronous request, and the browser opens a new HTTP request to execute the request. When it detects that the request has completed, it inserts the function into the end of the JS execution queue for processing.



The asynchronous processing can be seen in the example of the timing function shown in the figure. First, a 1 is printed, and then the setTimeout function is executed. The convention is to print a 2 and then a 3 after 0 seconds. The execution result is to print 1 first, then 3, and finally 2, indicating that the whole process has undergone an asynchronous process.



Javascript has a main execution thread, but setTimeout, IO operations, network asynchronous requests, etc., all have a callback function. These operations are implemented through the browser API. When the timer runs out or the request results come back, The callback function is pushed into a javascript task queue, and when the main javascript task is finished, the waiting code is retrieved from the task queue for execution. The specific process is summarized as follows:

(1) All synchronization tasks are executed on the main thread, forming an execution context stack.

(2) In addition to the main thread, there is a task queue. Whenever an asynchronous task has a result, an event is placed in the “task queue”.

(3) Once all synchronization tasks in the “execution stack” are completed, the system reads the “task queue” to see what events are in it. Those corresponding asynchronous tasks then end the wait state, enter the execution stack, and start executing.

(4) The main thread repeats step 3 above.

 

Asynchronous solution

Now that you know Javascript single-threaded and asynchronous, how do you write engineering code for everyday work? How can a specific method be executed after an asynchronous request is complete? Currently, asynchronous javascript programming has gone through several phases and is evolving.

Callback

It starts with the callback function, which is executed after the event is complete to complete the asynchronous operation. We can use a function like a variable, as an argument to another function, as a return result in another function, and call it in another function. When we pass a callback function as an argument to another function, we pass only the definition of the function and do not execute it in the argument. Once a function has a callback function defined in an argument, it can call (that is, call back) it at any time.



In our work, the longest contact is to send Ajax asynchronous request. After the first request is sent, the second request is processed with its parameters, and then the third request is requested with the parameters of the second request. The three-layer nested code is shown in the figure.



The example of mongoDB in Node.js is shown in the figure, with six layers nested. When multiple asynchronous transactions rely on multiple levels of dependency, the callbacks form multiple levels of nesting and the code becomes pyramidal. While this solves the asynchronous problem, it makes the code difficult to read and makes debugging and refactoring risky.

Promise

Promises are more reasonable and powerful than traditional solutions and are a better asynchronous solution. Promise objects have the following four characteristics:

1. A promise can be in three states: pending, completed and Rejected.

2. The promise state can only change from “wait” to “complete” or “reject”, but not reverse. Meanwhile, the “complete” and “reject” states cannot switch to each other.

3. Promise objects must implement then methods, and then must return a promise. The SAME PROMISE’s THEN can be called multiple times (chained), and the callbacks are executed in the order in which they were defined.

4. The then method takes two arguments, the first a callback on success, called when a promise transitions from a “wait” state to a “complete” state, and the second a callback on failure, called when a promise transitions from a “wait” state to a “reject.



As shown, create a Promise object on the left, execute it after 2000 seconds, and return the Promise object. Promises are immediately executable, and when an object is created, the code executes once before it is completed or rejected. On the right, you can see that the creation of the Promise object is printed and the promise object is successfully generated. When executing THEN, the success status has already been returned, so the successful callback function is always executed.



So how does Promise address asynchrony? What are the characteristics? Details are as follows:

(1) The code looks more logical and readable.

(2) Promise does not change the nature of ASYNCHRONOUS JS execution, and there is even a hint of callback in the writing.

As shown in the figure, it is a chain call. After the first request is successful, the next one will be executed. If any request fails, the error will be directly caught and printed out.

Generator

We want to write asynchronous code synchronously so that the logic is clearer and the code is less. To achieve this goal, we evolved the Generator solution.



Generator functions are special and more difficult to understand than promise and callback. Syntactically, Generator has the following properties:

(1) When defining Generator, use function*.

(2) Generate a Generator object when used.

Next () activates the pause state and starts executing internal code until yield is reached, which returns the result of the current execution and, remembering the context in which it was executed, pauses.

(4) Repeat step 3 at.next().

As shown in the figure, test=add(5) is defined first. The function is not executed, but a generator object is generated. The pause state is activated only when.next() is executed, and the internal code is executed. Time out. Surrender control. When.next() is executed again, find the second yield, remember the context again, pause, surrender control, and repeat.



As shown in the figure, encapsulate an asynchronous task, define a Generator object, perform the request, hand over control, and determine whether to continue with the promise. After an asynchronous event occurs, control is first given to someone else and the program executes other code. When the asynchronous event is complete, control is taken back and other operations continue. The Generator requires an automatic actuator to be used in conjunction with the Generator for normal asynchronous processing. With an automatic actuator, asynchronous requests can be written asynchronously, directly writing all requests together.

Async/Await



Through further evolution, async and await functions emerge. Their personality traits are as follows:

(1) async means that this is an async function and await can only be used in this function.

(2) await means to wait for the operation following await to complete before executing the next line of code.

(3) Preferably a time-consuming operation or an asynchronous operation should be followed by an await operation.

(4) Await must be followed by a Promise object, which if not will be transformed into a completed Promise



Async is written similarly to generator and is essentially a syntactic sugar for generator. Its built-in executor, with better semantics, wider applicability. And the return value is Promise.

This article is organized by the cloud habitat volunteer group MAO He, editor baijian