Understanding of async and await, internal principle

Async is a syntactic sugar for and an improvement on Generator functions

  1. An async implementation wraps Generator functions and auto-actuators in a single function.
async functionfn(args) { // ... } // is equivalent tofunction fn(args) {
  return spawn(function* () {//... }); }Copy the code

The spawn function is the auto-executor, and an implementation of the spawn function is given below

function spawn(genF){
    returnNew Promise((resolve, reject)=>{const gen = genF()function step(nextF) {
            let next
            try {
                next = nextF()
            } catch(e){
                return reject(e)
            }
            if(next.done){
                return resolve(next.value)
            }
            Promise.resolve(next.value).then((v)=>{
                step(()=>{return gen.next(v)})
            }, (e)=>{
                step(()=>{return gen.throw(e)})
            })
        }
        step(()=> {return gen.next(undefinex)})
    })
}
Copy the code
  1. Better semantics

Async and await are semantic clearer than asterisks and yield. Async means that an asynchronous operation is taking place in a function, and await means that an expression immediately following it needs to wait for the result.

  1. Wider applicability

According to the CO module, yield can only be followed by Thunk or Promise objects, while async can be followed by await and Promise and primitive values (values, strings, and Booleans). Resolve (next. Value)) in the spawn function.

  1. The return value is Promise

The then method can be used to specify the next action, rather than the return value of a Generator function being an Iterator

Async is the syntactic sugar of a Generator function, you may ask. What is a Generator function?

Generator function syntax: Focus on * and the keyword yield

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();
Copy the code

HelloWorldGenerator does not execute, but returns an iterator object. Next, you must call the next method of the iterator object to move the pointer to the next state

hw.next()
// { value: 'hello'.done: false }

hw.next()
// { value: 'world'.done: false }

hw.next()
// { value: 'ending'.done: true }

hw.next()
// { value: undefined, done: true }
Copy the code

Yield provides a function that can be paused because the Generator returns an iterator object that only calls the next method to iterate over the next internal state. The yield expression is the pause flag.

The Generator and coroutines

A coroutine is a way of running a program that can be implemented with either a single thread or multiple threads.

  • Coroutine concept:

When a thread (or function) is halfway through execution, it can suspend execution, hand over execution to another thread (or function), and resume execution later when it takes back execution. This thread (or function) that can execute in parallel and exchange execution rights is called a coroutine.

  • Coroutines differ from normal threads:
  1. Normal threads are preemptive, competing for CPU resources, whereas coroutines are cooperative,
  2. Next, multiple normal threads can be running at a time, while only one coroutine is running and the others are suspended.

Generator functions are ES6’s incomplete implementation of coroutines. Generator functions are referred to as “semi-coroutines”, meaning that only the caller of the Generator function can give the execution of the program back to the Generator function. If it is a fully executed coroutine, any function can continue execution of the suspended coroutine.

Generator and Context

A Generator function that executes the context in which it is generated. When yield is encountered, it exits the stack temporarily, but does not disappear, and all variables and objects inside it are frozen in their current state. When the next command is executed on it, the context is added to the call stack again, and frozen variables and objects resume execution.

The realization of the Generator

Refer to alloyTeam’s article for details

Instead of additional support from the engine at the bottom, the JS Generator is written to allow functions to be paused and executed sequentially. The two key points in implementing a Generator are to preserve the context information of a function and to implement a sophisticated iterative approach that allows multiple yield expressions to be executed in order to implement Generator characteristics. Basically using a state machine model made up of switch cases, in addition to using closure techniques to store generator function context information.

Regenerator wraps generator functions in utility functions, adding methods such as next/return to them. At the same time, the returned generator object is wrapped so that the calls to next and other methods end up in the state machine model composed of switch cases. In addition, the closure technique is used to save generator function context information.

This process is similar to the yield keyword in C#, which uses a compiler conversion approach, a state machine model, and the function context information. Finally, the yield keyword implements new language features.

This link shows the transformed code: this online address