Come on, boy

1. Fundamentals

Async/await is essentially a syntactic sugar for generator

  1. Built-in actuators do not require manual execution of the next() method

2, the generator

  1. Before we look at async and await let’s look at generator functions (es6.ruanyifeng.com/#docs/gener…
  2. ES6 introduces Generator functions that suspend the execution flow of functions using the yield keyword and switch to the next state using the next() method, providing the possibility to change the execution flow and thus providing a solution for asynchronous programming.
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
const hw = helloWorldGenerator();
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
  1. */yieldandasync/awaitIt actually looks very similar
  2. butGeneratorFeatures:
    • GeneratorThe generator object is returned
    • Need to call manuallynext()To proceed to the next step
  3. To recallasyncawaitThe characteristics,
    • async/awaitBuilt-in actuator, do not need to manually callnext()The next step is automatically performed
    • asyncThe return value of the function isPromiseobject
    • awaitTo be able to returnPromisetheresolve/rejectThe value of the
  • Based on our understanding of the Generator, we can encapsulate a pseudo-function

3. Simulated implementation of async and await

To implement async and await I must solve the Generator automatic execution problem

function* myGenerator() {
    const res1 = yield Promise.resolve(1);
    console.log('res1', res1); / / 1
    const res2 = yield Promise.resolve(2);
    console.log('res2', res2); / / 2
    const res3 = yield Promise.resolve(3);
    console.log('res3', res3); / / 3
  }
  
  // Execute iterators manually
  const gen = myGenerator()
  gen.next().value.then(val= > {

    gen.next(val).value.then(val= > {

      gen.next(val).value.then(val= > {
   
        gen.next(val)
      })
    })
  })

Copy the code

MyGenerator has a bit of an async and await feel to it, but the above manual execution code is unwieldy and not reusable, so we have encapsulated it as an automatic execution function

function runGenerator(gen) {
  var g = gen()  // Since gen() gets the latest iterator each time, the iterator must be fetched before _next(), otherwise an infinite loop will occur
  function _next(val) {             // Encapsulate a method that recursively executes g.ext ()
    var res = g.next(val)           // Get the iterator object and return the value of resolve
    if(res.done) return res.value   // Recursive termination condition
    res.value.then(val= > {         //Promise's then method is a prerequisite for automatic iteration
      _next(val)                    // Wait for the Promise to complete and automatically execute the next, passing in the value of resolve
    })
  }
  _next()  // This is the first execution
}

Copy the code

And then rewrite it based on the function above

function* myGenerator() {
    const res1 = yield Promise.resolve(1);
    console.log('res1', res1); / / 1
    const res2 = yield Promise.resolve(2);
    console.log('res2', res2); / / 2
    const res3 = yield Promise.resolve(3);
    console.log('res3', res3); / / 3
  }
  
  const gen = runGenerator(myGenerator); // 
  console.log(gen); //undefined (还需要实现返回promise)
Copy the code
  • This gives us an initial implementation of async/await.
  • Simply put, it encapsulates an automaticgeneratorthenext()Execute _next() every time promise.then () is used to implement automatic iteration.

Return promise and exception handling

There are three problems based on the above code

  1. The autoexecute function needs to return a promiseduixiang
  2. Yield is currently followed by promise.resove (), and we need to support plain level type values
  3. Exception handling
function runGenerator(gen) {
  // Wrap the return value as a promise
  return new Promise((resolve, reject) = > {
    var g = gen()

    function _next(val) {
      // Error handling
      try {
        var res = g.next(val) 
      } catch(err) {
        return reject(err); 
      }
      if(res.done) {
        return resolve(res.value);
      }
      //res.value is wrapped as a promise to accommodate cases where yield is followed by a basic type
      Promise.resolve(res.value).then(
        val= > {
          _next(val);
        }, 
        err= > {
          // Throw an error
          g.throw(err)
        });
    }
    _next();
  });
}

Copy the code

And then do it again

function* myGenerator() {
    try {
        const res1 = yield Promise.resolve(1);
        console.log('res1', res1); / / 1
        const res2 = yield 2;
        console.log('res2', res2); / / 2
        const res3 = yield Promise.reject('error');
        console.log('res3', res3); / / 3
      } catch (error) {
        console.log(error)
      }
  }
  
  const gen = runGenerator(myGenerator); 
  console.log(gen); //promise
// Output 1, 2 error
Copy the code
  • Well, today’s introduction here, I will sum up today’s main content.
  • Promise’s programming model is still riddled with a lot of THEN methods, and while it solves callback hell, it’s still semantically flawed, with a lot of THEN functions in the code, which is where async/await comes in.
  • Using async/await makes it possible to write asynchronous code in the style of synchronous code because the underlying technology of async/await is usedgeneratorandPromise

1, the epilogue

Thank you all for your likes and attention