Write at the front: This is one I wrote about the JS series, an article will not just only speak a knowledge point, main hope you don’t feel like talking so many things suddenly, see will be upset, because I usually will have associated knowledge together, so, learn knowledge will make you feel very coherent, I also want to record their own learning through the series, While sharing their own learning, mutual encouragement! If possible, please also give me a like, your like can also make me work harder to update!

An overview of

  • Serving time: 15-20 minutes

  • Difficulty: Easy, don’t run, watch before you walk

  • Edible value: Learn how to use Generator functions other than promises to solve asynchronous operations, learn how to declare them and how to call them, and finally, learn how to use the syntax sugar based on them, Async/await, to more elegantly solve the problem of multiple calls to promises.

  • Foreshadowing knowledge:

    (1) to understand THE JS series (8) easy to understand Promise, learn the basic concept of Promise.

Promise Asynchronous Encapsulation

To start with an example of a Promise nesting, we’ll execute three interfaces in turn, getData1(), getData2(), and getData3().

getData1().then(res1= >{
  getData2().then(res2= >{
    getData3().then(res3= >{})})})Copy the code

Writing in this way can easily lead to layers of nesting, which can make later maintenance difficult, and the code feels like dough.

But there is also a way to call a Promise called a chained call. The main idea is that a.then() callback returns a Promise, so you can keep a.then() as follows:

getData1().then(res= >{
  console.log(res)  // data1
  return getData2();
}).then(res= >{
  console.log(res) // data2
  return getData3();
}).then(res= >{
  console.log(res) // data3
})
Copy the code

Chained callbacks are much clearer to write than nested callbacks, after all, promises were invented to solve callback hell. Let’s look at another solution, the Generator function.

Generator

  • The basic concept

The Generator function is an asynchronous programming solution provided by ES6, mainly because it can be interrupted in execution and then wait for a period of time before we wake it up.

  • The function definitions

First of all, look at the name, Generator function, it is not difficult to tell, this is a function, so in order to distinguish it from normal functions, it has a special * after the function.

In addition, it has a yield keyword inside, and its main function is to split the function, which you can think of as dividing the function into multiple steps, with each call to the function performing only one step. This is the above said to be interrupted, such as the next time to continue to wake up.

function* helloWorldGenerator() {
  yield 'result1';
  yield 'result2';
  return 'result3';
}
Copy the code
  • Asynchronous encapsulation

Next, we can use the newly learned Generator function to encapsulate the interface. By dividing the code, we can cut the dough into pieces to make it look the same as the synchronized code, and divide it according to the functional modules. Each method can only do one thing, which is more convenient for later maintenance. Since we don’t need to write nesting, based on the idea of design pattern, we can first take the three interface calls out of the code, to make the effect obvious, we put an output statement inside.

function getResult1(){
  return getData1().then(res= >{
    console.log('step1');
    / /...})}function getResult2(){
  return getData1().then(res= >{
    console.log('step2');
    / /...})}function getResult3(){
  return getData1().then(res= >{
    console.log('step3');
    / /...})}Copy the code

Then, write the encapsulation of the Generator function, and the code will now look exactly like the synchronized code. Of course, to make this obvious, we add an output statement at the beginning.

function* getGenerator(){
  console.log('start');
  yield getResult1();
  yield getResult2();
  yield getResult3();
}
Copy the code

Using the yield keyword, the getGenerator function is divided into three sections, calling each interface.

  • A function call

There are differences between the declaration of the Generator function and that of the normal function, but there are also differences in how the function is called. After we call the Generator function, the function does not execute, and instead of returning the result of the function’s run, it returns a pointer object to the internal state. By calling the next() method on this pointer object, the execution starts from scratch, and stops until the first yield is reached. Until the next next() method, the execution continues from where it left off, and the next yield stops.

In summary, the yield keyword breaks the Generator function into several steps, while the next() method calls the Generator function, executing only one step at a time in the order of execution

So, as we said above, we first generate a pointer object and find that the function does not execute because there is no output from the console.

let hw = getGenerator();   // Only the pointer object is generated, not executed
Copy the code

Next, the first call to the next() method, that is, it will start from the beginning, output start, then the first yield, getResult1(), output step1, and then stop.

hw.next();     // Output start first, then step1
Copy the code

Next, the second call to the next() method will start after the last getResult1, and stop after the second yield, which is getResult2, output step2

hw.next();     / / output step2
Copy the code

(3) the third time is the same, output step3.

hw.next();     / / output step3
Copy the code

Complete the entire Generator function execution.

  • Automatic iterator

Of course, the above manual next() may be tedious, I am tired of defining a Generator function, and I have to make my wife as a pointer object, and then step by step to call next(), really tired, so let’s write an automatic iterator, Let the code step through the next() method for us.

function runGenerator(gen) {
    var it = gen(), ret;

    // Create a recursive function that executes immediately
    (function iterate(val){
        ret = it.next(val);

        if(! ret.done) {// If you can get a promise instance
            if ("then" in ret.value) {
                // Call iterate recursively right in its then methodret.value.then( iterate ); }}}) (); }Copy the code

Next, we will call the function again, and you can see that the function will run out automatically

runGenerator(getGenerator);  // start step1 step2 step3
Copy the code

The reason we can use this automatic iterator is that the Generator function has a done flag during execution, and when it is true, it is finished.

If you want to learn more about Generator functions, check out Yifeng Ruan’s tutorial

async / await

Having said all that, we can see that after the encapsulation of Generator functions, the code is clear, and the structure is fragmented, but the tedious function declaration, and also have to make an iterator to call the function, inevitably make the code more tedious.

The reason I’m going to go to so much trouble with Generator functions is that async/await, which I’m going to talk about next, doesn’t have that kind of trouble at all, but there’s a connection between the two.

So, it’s time for async/await in glory. We replace the Generator function with async await

async function hw(){
  console.log('start');
  await getResult1();
  await getResult2();
  await getResult3();
}
Copy the code

As you can see, the declaration is similar to Generator functions, except that * is replaced with async and yield is replaced with await.

Async does the same thing as *, to differentiate it from a normal function, to show you that it’s not a normal function, it’s an asynchronous function.

The await keyword tells you that I am now going to perform an asynchronous operation, I will wait for the rest of the code and when I am finished, you will get the await.

Not only are async/await functions declared in the same way as Generator functions, they are related because async/await functions are syntax-sugar based on Generator functions.

Of course, this is more than just declarative syntax sugar. Function calls have an overwhelming advantage over Generator functions, which need to be called in the same way as simple functions without cumbersome iterators to run them.

hw();    // start step1 step2 step3
Copy the code

This is by far the most elegant solution to asynchronous callbacks, but it also supports try/catch error catching. Of course, Generator functions are also supported.

However, since interface calls now use Axios, error catching on interface calls is typically handled directly in axiOS interceptors.

Series directory

  • This article understands JS series (a) compilation principle, scope, scope chain, variable promotion, temporary dead zone

  • This article understands JS series (2) JS memory life cycle, stack memory and heap memory, depth copy

  • JS series (3) to understand the garbage collection mechanism, memory leaks, closures

  • Understand JS series (four) closure application – Currie, partial function

  • Understand JS series (five) closure application – tremble, throttling

  • JS series (6) micro task and macro task, Event Loop

  • JS series (7) constructor, new, instance object, prototype, prototype chain, ES6 class

  • JS series (8) easy to understand the Promise

  • Learn more elegant asynchronous solutions in JS series (9)