preface

Asynchrony is an important part of JavaScript, and the main purpose of asynchrony is to “put away” time-consuming code so that it does not block synchronous code, and then process the results of asynchronous code through callback functions. However, there is an important problem when using asynchrony in real work, namely the order of multiple asynchronous codes.

If you’re making multiple Ajax requests, and the second request takes the result of one request, you need to order the asynchronous code so that the second request is executed after the first one. This is actually a slightly weird requirement when you think about it, to “synchronize” asynchronous code. But a number of new features in ES6 make this easier. We can use Promise, Generator, or async/await to fulfill the above “synchronous asynchronous code” requirements. By comparing the three, asynchrony in ES6 can be better understood.

No more words, on demand. First, simulate an asynchronous fetch operation that returns a Promise (typically used) of the data type shown below

function getData(params) {
  return new Promise((resolve) = > {
    / / ajax
    setTimeout(() = > {
      resolve("result: " + params);
    }, 500);
  });
}
Copy the code

Our requirement is that we first get the data with getData, and then perform getData on the data. The two operations should be in order, otherwise the parameters of the second time will be empty. —— above is the internship interview Tencent a question, at that time involved in the front end, even the question did not understand 😭.

This requirement will be implemented in three ways, following the ES6 asynchronous realm.

One realm: Order asynchronous code with promises

Promise was built for asynchrony, and.then can be a big relief from the “callback hell” of the past. Go directly to the code, see the comments for details.

getData("start").then((res) = > {
  Result: start
    console.log("stage1", res);
    getData(res).then((res) = > {
      Result: result: start
        console.log("stage2", res);
        console.log("all Done!!"); })})Copy the code

The print result is as follows:

stage1 result: start
stage2 result: result: start
all Done!!
Copy the code

As you can see above, it’s easy to understand, but it’s still a little uncomfortable to write asynchronously multiple times, and the “callback hell” problem isn’t completely solved. But that’s okay. There’s a new way.

Double state: Simplify promises with async/await

Async /await is the final form of this problem and should have been left at the end, but async/await is much cleaner and more common than Generator, so it is a second, as Generator does struggle to understand.

Async /await brothers, one by one.

Async: When it is placed before the keyword of a function, the return of the function can be wrapped with a Promise object that becomes a depressing state. Look at the code:

async function get1() {
  return 1;
}

get1(); // This is a big pity. {< big pity >: 1}
Copy the code

Await: you async can await a promise, I await a promise😊. Don’t delete the code above, keep using it.

await get1(); // The result is 1
Copy the code

The simplest features of the two brothers are opposites, but together they can easily solve the problem of asynchronous code synchronization. Code first:

// async/await,
async function asy_fn() {
  // The rest of the downstream code must wait until it gets the data.
  let stage1 = await getData("start");
  console.log("stage1", stage1);
  let stage2 = await getData(stage1);
  console.log("stage2", stage2);
  return "all Done!!";
}

let res_asy = asy_fn();
// Since the return value is Promise, the final result should be typed later
res_asy.then((res) = > {
    console.log(res)
})
Copy the code

Here are some details:

  1. Await can only do the above in an async function, and the return value of the function on the right side of await is a promise, and the asynchronous await caused by setTimeout is invalid.
  2. When await suspends code, the assignment to the left of the await is also unperformed and actually stops in the function to the right of the await.
  3. Because the asynchrony effect of await is at the same level as promise, at the microtask level. See “Await and Promise Priority with repeated horizontal hops” for details.

Triple state: Deconstruct async/await with Generator and Promise

Generator or iterator? He is a strange guy, seldom used in general business (I can’t understand how to do πŸ˜‚), but he is the principle behind async/await. And if you can master this knowledge flexibly, you can make a lot of things. This article will use this as the final boss to understand async/await.

Generator. A function declaration such as function* produces an iterator function. Each execution of the iterator produces an iterator object. The yield and return in this function are node by node, and next executes from one node to the next on the iterator. That’s how await stops on a line 😏.

The following is an example of show me your code:

function* gene() {
  yield 1;
  yield 2;
  return 3;
}

let g = gene(); // Generate an iterator object
console.log(g.next());
// { value: 1, done: false }
console.log(g.next());
// { value: 2, done: false }
console.log(g.next());
// { value: 3, done: true }
Copy the code

Next () yields {value, done}, in which value is the yield value or the return value of the function, and done is a Boolean, and whether the iterator is finished, that is, if there are any other yield or return statements to follow.

I feel like the next method is almost clear, but there is no 😏. The next function can actually pass arguments, which are the return value of the previous yield expression (somewhat convoluted). The following is an example:

function* gene() {
  let n1 = yield 1;
  console.log(n1);
  let n2 = yield 2; 
  console.log(n2);
  return 3;
}

let g = gene();
console.log(g.next());
console.log(g.next("res for yield 1"));
// the yield function returns the input value for the next run time,
// Each time.next is executed until yield and the rest.
// Neither the previous assignment nor yield itself returns a value
console.log(g.next("res for yield 2"));
Copy the code

It’s a little hard to understand, because the arguments in next run up. But it’s better to think of each next as a separate function. When we get to the second next, we actually execute the following code:

// console.log(g.next("res for yield 1"));
/ / equivalent to
function next(param) {
  let n1 = param;
  console.log(n1);
  yield 2;
}
Copy the code

It is clear to understand the Yeild return value in blocks.

How to use Generator to realize “asynchronous code synchronization”. The code:

function* gen() {
  let stage1 = yield getData("start");
  console.log("stage1", stage1);
  let stage2 = yield getData(stage1);
  console.log("stage2", stage2);
  return "all Done!!";
}

function run(generator, v) {
  let { value, done } = generator.next(v);
  if(! done) { value.then((res) = > {
      run(generator, res);
    });
  } else {
    console.log(value); }}let res = run(gen());
Copy the code

Implement a Gen iterator first, and generate an iterator instance to run automatically in the run, after all, writing next one by one is too silly.

No, the current generator implementation is very close to async/await implementation, but async returns promises and run returns strings. A few changes can achieve the same effect. So the following code is “How do I simulate async/await with Generator and Promise?” , i.e. Title async/await = Promise + Generator.

function* gen(stage0) {
  let stage1 = yield getData("start");
  console.log("stage1", stage1);
  let stage2 = yield getData(stage1);
  console.log("stage2", stage2);
  return "all Done!!";
}

function run(generator, v) {
  let { value, done } = generator.next(v);
  return new Promise ((resolve, reject) = > { / / a promise
    if(! done) { value.then((res) = > {
        return run(generator, res).then(resolve);
        });
    } else {
        return resolve(value); // the gen function returns}}); }let res = run(gen());
res.then((res) = > {
    console.log(res)
})
Copy the code

The above code can be equivalent to code in the duality, which is the implementation principle behind async/await. Hence the saying.

Async /await is a syntactic sugar for a Promise +generator+run function.

Afterword.

ES6 asynchronous Generator is less used, but this is a key step in understanding async/await. Fill in this gap and cognition is complete.