preface

I have always had a limited understanding of async/await, which sometimes makes me confused when the scene is more complex. So this accumulation summed up. I think this time can be patient, quiet watching is fruitful. If it is helpful to you, please also like to follow to encourage, thanks in advance. Before we say async/await, let’s first look at generators. You often encounter the phrase “async/await” on the Web: async/await is the syntactic sugar of the generator. How do you understand this sentence? As we go through the steps, let’s first address a few nouns associated with generator.

Iterators, iterator objects, generators, iterable objects

  • Iterator, iterator object

I understand iterators and iterator objects to be the same, because an iterator is itself an object.

An iterator is any object that can be used to generate an iteration protocol (or rule) using the next method

In my own words:

1: An iterator is an object that has a method next.

2: This next returns an object that reaches the iterated protocol or rule, which is essentially an object containing the value and done fields.

  • The generator

A generator is a function* that can be called to generate an iterator.

  • Iterable objects

An iterable object is an object that has iterative behavior.

What is iterable behavior, as I understand it:

1: This object has a [Symbol. Iterator] attribute and the value is a function. It’s not a string, but it’s a little bit clearer in code.

Const obj = {[Symbol. Iterator]: () => {}} // const obj = {"Symbol.Copy the code

2: [Symbol. Iterator] This function returns an iterator.

There are several ways to write generator

The reason why I want to say this is mainly because the generator is used less, so sometimes in a panic, it will short circuit, or because I did not summarize, so I will write all the following.

  • A named function
function* init() {}
Copy the code
  • Anonymous functions
function* () {}
Copy the code
  • Function expression
const init = function* () {}
Copy the code
  • Arrow function
// Remember that generator cannot write 2021/03/24 with the arrow function yet
const a = *() = > {};
Copy the code

Simple use of generator

To illustrate async/await, you can go to the es6 course of Ruan Yi-feng to get familiar with it.

const init = function* (x) {
    const y = yield (x + 1);
    const z = yield (y / 3);
    return z;
}
a = init(5);
a.next(); // {value: 6, done: false}
a.next(); // {value: NaN, done: false}
a.next(); // {value: NaN, done: true}
Copy the code
const init = function* (x) {
    const y = yield (x + 1);
    const z = yield (y / 3);
    return z;
}
a = init(6);
a.next(6); // {value: 7, done: false}
a.next(6); // {value: 2, done: false}
a.next(6); // {value: 6, done: true}
Copy the code

A promise version — that’s important

const test = function* () {
  const a = yield new Promise((resolve, reject) = > {
     setTimeout(() = > {
     resolve(3);
}, 2000);
});
return a;
}

const a = test();
console.log(a.next()); // {value: Promise, done: false}
console.log(a.next());
Copy the code

Construct a self-executing next function

The promise version of the generator shown above is primarily to illustrate the principle of async/await, which will be discussed later. Now we’re going to implement a self-executing next function, because async/await is self-executing. Let’s start with a simple shelf.

const exec = g= > {
    const {value, done} = g.next();
    if(! done) {returnexec(g); }}const value = exec(g);
Copy the code

A slightly stronger version

The above implementation is a bit bad, mainly for the future I can know how to change step by step, the above function does not pass the value every time, now we are under the revision.

const g = generator();

const next = value= > {
    const {value, done} = g.next(value);
    
     if (done) {
        return value;
     }else {
       next(value)
    }
}

next();
Copy the code

It’s important to remember this function if this version is better.

Promise version

In order to understand asyC /await, let’s make a promise version

const g = generator();
const next = value= > {
   const {value, done} = g.next(value);

    if(! done) {// Value is a promise
        value.then(val= >{ next(val); }}})Copy the code

Start async/await cognition

Before I say async/await I’m going to throw out three of my own ideas, which you can adopt if it helps you understand better, and skip the other way around.

1: Async returns a promise object

2: await is equivalent to yield and is placed in generator functions

3: Await is equivalent to wrapping a Promise object and putting the value in resolve. So let me describe this in a little bit of code

const test = async function() {
   const a = await 123;
   const b = 5 + a;
   console.log(b);
}
// Change the above code to what I know
const test = function() {
   Async returns a promise
   return new Promise((resolve, reject) = > {
          const generator = function* () {
             // Recognize that 2 await is equivalent to a yield and put it in a generator function
            // Recognize that 3 await is equivalent to wrapping a promise object and putting the value in resolve
              const a = yield new Promise((resolve, reject) = > {
                   resolve(123);
                });
               const b = 5 + a; 
               console.log(b);
             }
          // The self-executing function should be added
          const g = generator();
          const next = val= > {
              const {value, done} = g.next(val);
              if (done) {
                    resolve(value);
              } else {
                value.then(val= > next(val));
             }
          }
            next()
})
}
Copy the code

Async /await is a combination of generator and promise. Of course, the source code is certainly not my such. Now we can see why async/await is the syntax-sugar of genarator. Have wrong place can point out, communicate together, below use a practical point complex point example to verify, consolidate below.

Version at work

First encapsulate a request function

const getList = async url => {
  return await fetch.get(url);
}
Copy the code

The fetch. Get request function is simulated with promise to make it more complex

// I think axios.post().then() is the final encapsulation of a promise
const request = (url) = > {
   return new Promise((resolve, reject) = > {
      setTimeout(() = > {
         resolve({result: [1.2.3].code: 1});
       }, 500); // Simulate the request
});
}
const fetch = {
     // The URL is virtual
     get: (url) = > {
         // Why use request on the enclosing layer, in addition to the extension, but also to make this example more complex
        // Because then is used
         return request(url).then(res= > {
               if (res.code === 1) {
                  return{... res,success: true}; }}); }}Copy the code

Actual business code

class Test extends React.Components {
    async componentDidMount() {
       const data = await getList(url);
       console.log('When do I execute?');
       console.log(data); }}Copy the code

The above example componentDidMount is an async/await, while getList is an async/await, and the return of fetch. Get is request(URL).then(). Does the following print “when do I execute” wait until data has a value?

The answer is yes

Analysis of the

Analysis of the 1

“ComponentDidMount” is “async/await”. “getList” is “componentDidMount”. But a lot of people might get lost in the “fetch. Get” part. This function already returns, so data should have no value. First of all, async/await is not deeply understood, and then promise is not deeply understood. The following three levels of cognition are used to analyze.

Analysis of 2

2-1

According to our analysis above, let’s transform the first function

 async componentDidMount() {
       const data = await getList(url);
       console.log('When do I execute?');
       console.log(data);
    }
// Change to the following
const componentDidMount = function() {
   return new Promise((resolve, reject) = > {
          const generator = function* () {
              const data = yield new Promise((resolve, reject) = > {
                   resolve(getList(url));
                });
               console.log('When do I execute?');
               console.log(data);
             }
          // Convert to self-executing function form. See above
          const g = generator();
          const next = val= > {
              const {value, done} = g.next(val);
              if (done) {
                    resolve(value);
              } else {
                value.then(val= > next(val));
             }
          }
            next()
})
}
Copy the code
  • If you want to execute console.log(‘ when do I execute ‘), do you have to wait until value.then? We can see that value is the promise object after yield.
         new Promise((resolve, reject) = > {
                   resolve(getList(url));
                });
Copy the code

Then the execution of value.then depends on resolve(getList(URL))

  • Let’s look at getList(URL), what this function returns. GetList is also an async/await. Async returns a Promise object, so it is quite resolve(new Promise()).

  • I highly recommend checking out my previous article on the Promise source code. If A promise (called A) is also A Promise (called B) within resolve, then the execution of A depends on when B executes resolve.

  • Console. log(‘ when do I execute? ‘). When does this sentence execute depends on the timing of getList resolve

2-2

So we go to getList and we see that it’s actually the same thing as componentDidMount, so let’s just tweak it

const getList = async url => {
  return await fetch.get(url);
}
// After the modification
getList = function() {
   return new Promise((resolve, reject) = > {
          const generator = function* () {
             yield new Promise((resolve, reject) = > {
                   resolve(fetch.get(url));
                });
         
             }
          // Convert to self-executing function form. See above
          const g = generator();
          const next = val= > {
              const {value, done} = g.next(val);
              if (done) {
                    resolve(value);
              } else {
                value.then(val= > next(val));
             }
          }
            next()
})
}
Copy the code
  • If (done) {resolve(value)}, the first yield must be true.

  • Let’s look at the first yield, which is still a Promise object. Resolve (fetch. Get (URL)) must be executed first to execute value.then. That’s a little familiar.

  • Fetch. Get returns request(URL).then(). The crux of the question is, is this a promise? If there’s nothing to say, just return. If it’s a promise, we need to keep analyzing it, because our 500-millisecond pause hasn’t started yet.

  • For those of you who know the promise source code, then simply returns a new Promise object.

  • Resolve (fetch. Get (URL)) returns a new promise. The execution of resolve depends on whether the promise object returned by fetch. In other words, see request(URL).then() when the promise will resolve.

2-3

Explain promise again. Then returns a promise for what execution, in fact, then when to execute. Let’s put in a little bit of the source code for “THEN” that I wrote earlier.

if (self.info.status === "pending") {
      self.onFulfilledArr.push((data) = > {
        setTimeout(() = > {
          try {
            let value = data;
            value = onFulfilled(value);

            resolve(value);
          } catch(e) { reject(e); }}); });Copy the code

We will see that as soon as the then function (the first callback) is executed, the value is retrieved directly and then resolved. Request (URL).then() : if (request(URL). Then () : if (request(URL). If (request(URL). Maybe you’re a little out of line right now so let me get this straight.

2-4

Async /await(compontDidMount)—–> Must wait until getList() for the Promise object to resolve before continuing next(), The resolve of getList() is to wait until fetch. Get () or request(URL).then(). The request(URL).then() promise must wait until the then function executes —–> and the then callback must wait until the promise object returned by the request(URL) is resolved —–> The request(URL) returns a promise object 500 milliseconds later. Resolve —–> The entire code will wait until the generator function is resolved 500 milliseconds later before going to next.

conclusion

Looking at the summary above, you may have a new understanding of async/await. Many online articles are in-depth source explanation. But my article is to convert async/await to promise + Generator. Of course, if you want to know how the generator is implemented, you can search the Internet.

Refer to the article

Async/Await/Generator implementation principle