The previous chapter discussed Generator knowledge which is a foundation for understanding async/await. Async /await is a further optimization of synchronization for Promise processing of asynchronous tasks. It has a simple syntax and is a favorite syntactic sugar used by front-end staff. So what exactly is behind async/await:

  1. async/awaitWhat are the advantages?
  2. asyncFunctionConstructor.
  3. async/awaitSyntax, and error catching.
  4. async/awaitOperating principle.
  5. throughPromiseandGeneratorFunction to implement aasync/await.

1. What are the advantages of async/await

  1. async/awaitKeywords allow us to write based in a more concise wayPromiseAsynchronous behavior without the need for intentional chain callspromise.
  2. More in line with the human way of thinking, usePromise“, need to write.then()Waiting for multiple callbacks to get the internal data, which is still somewhat inconvenient to read,async/awaitThe order of writing is the order of execution and is easy to understand.
  3. PromiseIs a functional programming paradigm that encapsulates asynchronous processes,async/awaitCoroutine based mechanism is a more accurate description of asynchronous process.

2. AsyncFunction constructor

Like Generator, async functions are new asynchronous function objects created through constructors. And it’s not a global object.

let AsyncFunction = Object.getPrototypeOf(async function(){}).constructor
async function asyncExpression(){}
let asyncFn = new AsyncFunction()
console.log(asyncExpression instanceof AsyncFunction) // true
console.log(asyncFn instanceof AsyncFunction) // true
// However, it is not recommended to use the new instance in this way, as is the case when creating arrays using literals directly.
// Because function expressions are more efficient, asynchronous functions are parsed by the interpreter along with other code, while using the new body is parsed separately.
Copy the code

The AsyncFunction constructor can be called by omitting new and the effect is the same.

3. Async /await syntax and error catching

When we create an async function, async must return a Promise.

async function foo() {
   return 1
}
/ / equivalent
function foo() {
   return Promise.resolve(1)}Copy the code
  1. asyncAsync: async: async: async: async: async: async: async: async: async: async: async: async: async: async: asyncasyncIt’s redundant.
  2. It has to do withGeneratorFunctions are very similar, you can have more than one insideawaitPerform toawaitThe expression pauses and blocks the entireasyncFunction and waitawaitAfter the expressionPromiseAsynchronous results until obtainedPromiseThe returnedresovlePost-result recoveryasyncFunction execution.
function promiseFn(){
    return new Promise((resovle, reject) = > {
        setTimeout(() = > {
          resovle('success')},2000)})}async function foo() {
   let a = await promiseFn();
   console.log(a);
   console.log('after await'); 
}
console.log(foo())  // Output success, after await after 2 seconds
Copy the code
  1. awaitMust be inasyncFunction is executed internally, otherwise a syntax error is reported.
  2. Due to theawaitYes Synchronous execution, ifawaitAfter the asynchronous task error, will cause the following code cannot execute, so usetry/catchTo catch errors, allowing subsequent code to continue execution.
function promiseFn(){
    return new Promise((resovle, reject) = > {
        setTimeout(() = > {
          reject('error')},2000)})}async function foo() {
    try{
        let a = await promiseFn();
    }catch(e) {
        console.log(e); // Error is output after two seconds}}Copy the code

5. Await in async function will block the code behind the async function body when executing the Promise asynchronous task, but async function call will not block, all blocks inside it are wrapped in a Promise object to execute asynchronously. This is why await must be used in async functions.

function promiseFn1() {
    return new Promise((resovle, reject) = > {
        setTimeout(() = > {
              resovle('1success')},2000)})};function promiseFn2() {
    return new Promise((resovle, reject) = > {
        setTimeout(() = > {
            resovle('2success')},1000)})}async function foo() {
    let a = await promiseFn();
    console.log(a);
}
foo()
promiseFna().then(res= > {
    console.log(res);
})
// Output after one second: 2SUCCESS
// Output after two seconds: 1SUCCESS
Copy the code

6. Multiple await requests without dependencies on each other can be made at the same time with promise. all requests to speed things up.

function promiseFn1() {
  return new Promise((resovle, reject) = > {
    setTimeout(() = > {
      resovle('success1')},10000)})}function promiseFn2() {
  return new Promise((resovle, reject) = > {
    setTimeout(() = > {
      resovle('success2')},5000)})}async function foo() {
    let res = await Promise.all([promiseFn1(), promiseFn2()])
    console.log(res);  // After 10 seconds ['success1', 'success2']
}
// Get all results in 15 seconds if two await are performed successively.
foo()
Copy the code

4. Operation principle of async/await

Async /await is a callback implemented by generator+yield control flow + Promise. The syntax is encapsulated. So to understand how generator+yield controls flow, I will briefly introduce the concepts of processes, threads, coroutines, and combine generator and async/await syntax to illustrate the implementation process:

process

  1. The simple idea is to start an instance of an application, open a browser, open vscode, and then start two processes.
  2. Broadly defined as: 1) it is a running activity of a program with independent functions on a certain data set. 2) It is the basic unit of dynamic execution of the operating system. Process is both the basic allocation unit and the basic execution unit.
  3. Each process has its own address space, including text area, data area, and stack.
  4. The process itself does not run and is a container for threads.
  5. Processes can be in five states: 1) create state 2) Ready state 3) execute state 4) blocked state 5) terminate state **

thread

  1. Vscode to edit the code, open the command window, open git trace, this is a thread, multiple processes work.
  2. A process has at least one main thread, and may have more child threads, otherwise there is no point in existence.
  3. Threads are the smallest unit of execution for an operating system, and threads and processes are scheduled by the operating system in line (CPU).
  4. A standard thread consists of the current thread ID, current instruction pointer, registers, and stack.
  5. Multiple threads in the same process can execute concurrently. (Concurrency: two or more threads jump and run at the same time, but only one thread is running at the same time)
  6. Thread state: 1) ready state 2) running state 3) blocked state.

coroutines

  1. A thread can have more than one coroutine.
  2. The scheduling of coroutines is completely controlled by the user.
  3. Coroutines have their own register context and stack.
  4. When the coroutine schedules the switch, the register context and stack are saved in other places, and when the switch is cut back, the previously saved register context and stack are restored. The direct operation of the stack basically has no kernel switching overhead, and you can access global variables without locking, so the context switch is very fast

Since JavaScript runtime is single-threaded, it uses a loop mechanism to execute code. To create A Generator function, you can create A coroutine and use yield to switch the coroutine to complete an asynchronous task. The running process is as follows: 1) The coroutine A starts to execute; 2) The execution of coroutine A is suspended halfway, and the execution power is transferred to coroutine B; 3) After a certain period of time, coroutine B returns the execution power; 4) Coroutine A resumes execution; Yield control when it encounters a yield expression, waits for the result to be executed, and when it gets the result, returns the execution authority and prints it. To understand how Generator works, compare async/await:

  1. createGeneratorFunction *, can be interpreted as createAsyncFunction of theasync.
function* gen(){}
async function asy(){}
Copy the code
  1. Execute to the point where you need to pauseyieldtoawaitIt has a package insidenext(), so no manual call is required. butawaitThe value returned passes throughasyncPackaging, alwaysPromise.
function* gen(){
    yield 1
}
async function asy(){
    await 1
}
Copy the code

So async/await is encapsulated by generator+yield control flow + Promise syntax.

5. Implement a simple async/await

// Defines a promise that simulates an asynchronous request by passing in the ++ argument
function getNum(num){
    return new Promise((resolve, reject) = > {
        setTimeout(() = > {
            resolve(num+1)},1000)})}// The auto-executor is called recursively if a Generator function is not completed
function asyncFun(func){
  var gen = func();

  function next(data){
    var result = gen.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }
  next();
}

// This is the Generator function that needs to be executed. The data inside will be called after the promise of one step has been fulfilled
var func = function* (){
  var f1 = yield getNum(1);
  var f2 = yield getNum(f1);
  console.log(f2) ;
};
asyncFun(func);
Copy the code