The introduction

Async /await is a great syntactic candy that is the ultimate solution to the asynchronous problem. Take it literally. Async means asynchronous and await means to wait, so understand async to declare that a function is asynchronous and await to wait for an asynchronous method to complete.

Async role

Async declares that function is an asynchronous function that returns a Promise object, and callbacks can be added using the then method. The value returned by the return statement inside the async function becomes an argument to the then method callback function.

async function test() {
  return 'test';
}
console.log(test); // [AsyncFunction: test] Async functions are instances of the [' AsyncFunction '] constructor
console.log(test()); // Promise { 'test' }

Async returns a Promise object
test().then(res= >{
  console.log(res); // test
})

Async returns a undefined promise object if async returns no value
async function fn() {
  console.log('No return');
}
console.log(fn()); // Promise { undefined }

// Async returns the same value as promise.resolve (), wrapping the return value as a Promise object, or returning the undefined Promise object if no value is returned
Copy the code

await

The await operator can only be used inside async function. If a Promise is passed to an await operator, await will wait for the normal processing of the Promise to complete and return its processing result, that is, it will block the following code, waiting for the Promise object result. If you are waiting for something other than a Promise object, the value itself is returned.

async function test() {
  return new Promise((resolve) = >{
    setTimeout(() = > {
        resolve('test 1000');
    }, 1000); })}function fn() {
  return 'fn';
}

async function next() {
    let res0 = await fn(),
        res1 = await test(),
        res2 = await fn();
    console.log(res0);
    console.log(res1);
    console.log(res2);
}
next(); Res1 is waiting for the promise result and blocks the following code.
Copy the code

Error handling

If an asynchronous operation following await fails, the Promise object returned by the async function is rejected.

async function test() {
  await Promise.reject('Wrong')}; test().then(res= >{
  console.log('success',res);
},err= >{
  console.log('err ',err);
})
// Err
Copy the code

The way to prevent errors is to put it in a try… Inside the catch block.

async function test() {
  try {
    await new Promise(function (resolve, reject) {
      throw new Error('Wrong');
    });
  } catch(e) {
      console.log('err', e)
  }
  return await('It worked');
}
Copy the code

Asynchronous operations following multiple await commands are best fired at the same time if there is no secondary relationship (i.e. no dependency).

let foo = await getFoo();
let bar = await getBar();
GetBar is executed only after getFoo is done

// Trigger the ↓

/ / write one
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

/ / write two
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;
Copy the code

Async/await an advantage

The advantage of async/await is to handle then chains consisting of multiple promises. The problem of using THEN to handle callback hell was mentioned in the previous Promise article. Async /await is a further optimization of promises. Assume that a service has multiple steps, and each step is asynchronous and depends on the execution result of the previous step.

// Suppose the form is submitted through two validation interfaces

async function check(ms) { // imitate asynchrony
  return new Promise((resolve) = >{
    setTimeout(() = > {
        resolve(`check ${ms}`); }, ms); })}function check1() {
  console.log('check1');
  return check(1000);
}
function check2() {
  console.log('check2');
  return check(2000);
}

// -------------promise------------
function submit() {
  console.log('submit');
  // After two checks, multi-level association promise values are deeply nested
  check1().then(res1= >{
    check2(res1).then(res2= >{
       /* * Submit the request */
    })
  })
}
submit();

// -------------async/await-----------
async function asyncAwaitSubmit() {
    let res1 = await check1(),
        res2 = await check2(res1);
        console.log(res1, res2);
        /* * Submit the request */
}


Copy the code

The principle of

Async functions are implemented by wrapping Generator functions and automatic actuators in one function.

async function fn(args) {
  // ...
}

/ / is equivalent to

function fn(args) {
  return spawn(function* () {
    // ...
  });
}
Copy the code
The /* * Generator function is a encapsulated asynchronous task, or a container for asynchronous tasks. * Where asynchronous operations need to be paused, use yield statements * To call a Generator function that returns a pointer object (this is the difference from normal functions). Calling the next method on the pointer object moves the internal pointer. * The next method is used to execute Generator functions in stages. Each time the next method is called, an object is returned representing information about the current stage (value and Done properties). The value attribute is the value of the expression following the yield statement and represents the value of the current phase; The done attribute is a Boolean value indicating whether the Generator has completed execution, that is, whether there is another phase. * /

// Understand the usage of generator
function* Generator() {
  yield '1';
  yield  Promise.resolve(2);
  return 'ending';
}
 
var gen = Generator(); // Return pointer Object [Generator] {}

let res1 = gen.next();
console.log(res1); // Return the current stage value {value: '1', done: false}

let res2 = gen.next();
console.log(res2); {value: Promise {2}, done: false}

res2.value.then(res= >{
  console.log(res); / / 2
})

let res3 = gen.next();
console.log(res3);  // { value: 'ending', done: true }

let res4 = gen.next();
console.log(res4); // { value: undefined, done: true }

Copy the code

Generator implements async functions

// Accept a Generator function as a parameter
function spawn(genF) {
  // Return a function
  return function() {
     // Generate a pointer object
     const gen = genF.apply(this.arguments);
     // Return a promise
     return new Promise((resolve, reject) = > {
          // Key has two values, next and throw, corresponding to gen's next and throw methods respectively
          The arg argument is used to yield the promise resolve value to the next yield
          function step(key, arg) {
            let result;

                // If an error is detected, reject the promise
                try {
                  result = gen[key](arg)
                } catch (error) {
                  return reject(error)
                }

                // gen.next() returns {value, done}
                const { value, done } = result;

                if (done) {
                      // If this is already done, resolve the promise
                      return resolve(value)
                } else {
                      // Call gen.next() every time except at the end
                      return Promise.resolve(
                        // This value corresponds to the promise after yield
                        value
                      ).then((val) = >step("next", val),(err) = >step("throw", err))
                }
          }
          step("next")}}}Copy the code

test

function fn(nums) {
  return new Promise(resolve= > {
    setTimeout(() = > {
      resolve(nums)
    }, 1000)})}/ / async function
async function testAsync() {
  let res1 = await fn(1);
  console.log(res1); / / 1
  let res2 = await fn(2);
  console.log(res2); / / 2
  return res2;
}
let _res = testAsync();
console.log('testAsync-res',_res); // Promise
_res.then(v= >console.log('testAsync-res',v)) / / 2

/ / function Generator
function* gen() {
  let res1 = yield fn(3);
  console.log(res1); / / 3
  let res2 = yield fn(4);
  console.log(res2); / / 4
  // let res3 = yield Promise.reject(5);
  // console.log(res3);
  return res2;
}

let _res2 = spawn(gen)();
console.log('gen-res',_res2); // Promise

_res2
.then(v= >console.log('gen-res',v)) / / 4
.catch(err= >{console.log(err)}) // res3 execution throws an exception


Copy the code

conclusion

Async /await syntax candy makes asynchronous code clearer and more readable, so roll it up. Generator can read about it if interested.