I’m Garfield at 🐱. If you like my article, please feel free to like it and share it

Async /await is an executor of a generator. To implement async/await we need to understand what a generator is.

1. What are generator functions

Generator is a function that generates an iterator, and the yield expression is a token that pauses execution. The next method of the iterator object must be called to resume execution of the function so that the pointer moves to the next state.

function *gen() {
  yield 1;
  yield 2;
  return "ending";
}

let g = gen();

g.next(); // {value: 1, done: false}
g.next(); // {value: 2, done: false}
g.next(); // {value: 'ending', done: true}
g.next(); // {value: undefined, done: true}
Copy the code

In the above code, the gen function is called first to return an iterator, and the next method is called four times.

On the first invocation, the generator function starts executing until the first yield expression is encountered, and then evaluates the expression following the yield expression as the value of the value attribute, which is false, indicating that the traversal is not complete.

On the second invocation, the generator function takes the input (if any) of the next method as the return value of the yield expression, executes to the next yield expression where the last yield expression left off, and evaluates the expression after the yield expression. Returned as the value of the value attribute, the done attribute has a value of false, indicating that the traversal is not finished.

On the third call, the generator function takes the input (if any) of the next method as the return value of the yield expression, and then executes from where the last yield expression left off until the return statement (or the end of the function if there is no return statement). The value of the expression following the return statement is then returned as the value of the value attribute. The value of the done attribute is true, indicating that the traversal is complete.

The next method returns the value of the object as undefined and the done property as true. Any subsequent calls to the next method will return this value.

Note:

  • yieldThe expression after the expression will only be innextMethod is evaluated when it moves the pointer to this line;
  • yieldThe expression itself does not return a value,nextA method can take an argument that will be used as the previous oneyieldThe return value of the expression;

The generator functions are actually very similar to the Iterator interface and can use for… Of traversal, which can also be expanded into an array using the extension operator. In contrast to the Iterator interface, generator has a next method, a return method to terminate subsequent processes, and a throw method to throw errors.

2. Simplest actuator

With this in mind, we can implement a generator actuator. For example, we have the following generator function:

const fullfilled = (param) = > {
  return new Promise(resolve= > setTimeout(resolve, 1000, param))
}

function *effect() {
  const a = yield fullfilled("Test = = = = = = 1");
  console.log(a);
  const b = yield fullfilled("= = = test 2 = = =");
  console.log(b);
}
Copy the code

First, let’s see how manual execution works:

const gen = effect();

// Start executing generator
gen.next().value.then(res= > {
  // Use the result of the first Promise as the yield expression return value
  // Print "=== test 1==="
  gen.next(res).value.then(res= > {
    // Return the result of the second Promise as the yield expression
    // Print "=== test 2==="gen.next(res); })})Copy the code

With this in mind, implementing the actuator is easy:

function asyncToGenerator(fn) {
  const gen = fn();
  let step = (val) = > {
    const { value, done } = gen.next(val);
    // If the iteration is not finished, then method is used to get the result of the previous step
    // Then recursively call step for the next iteration! done && value.then(step); } step(); }Copy the code

3. Return to the Promise

The above code just implements the executor, not async/await. We know that async will return the value of the return inside the function wrapped in a Promise, so we implement this function:

function asyncToGenerator(fn) {
  // Return a Promise instance
  return new Promise((resolve, reject) = > {
    const gen = fn();
    let step = (val) = > {
      const { value, done } = gen.next(val);
      if (done) {
        // If the iteration ends, resolve the final result
        // undefined if the function returns no value
        resolve(value);
      } else {
        // If the iteration is not finished, then method is used to get the result of the previous step
        // If the Promise succeeds, step is recursively called for the next iteration
        // If the Promise fails, reject is rejectedvalue.then(step, reject); } } step(); })}Copy the code

4. Support non-Promise types

Normally, the right side of the await command is a Promise object. If it is not a Promise object, the corresponding value is returned:

function asyncToGenerator(fn) {
  return new Promise((resolve, reject) = > {
    const gen = fn();
    let step = (val) = > {
      const { value, done } = gen.next(val);
      if (done) {
        resolve(value);
      } else {
        // This is not the case
        // Wrap a Promise directly around a non-PROMISE type
        Promise.resolve(value).then(step, reject); } } step(); })}Copy the code

reference

Minimal implementation of Handwriting Async await (20 lines)