We used to use Promise to solve the problem of asynchronous request data, but we didn’t know that ES8 async await code can write asynchronous code like synchronous, so we went to explore the principle of using Generator, and today we implement a simple async await

What is the Generator

My understanding is that the Generator function returns an iterator object, so let’s write a small example.

function* my() {
    yield console.log(1)
}
var g = my()
console.log(g)Copy the code

The printed result is:

You can see that it has a next method, and when we use the next method, we will execute the yield statement, so let’s print out g.next(),



It works.

yield console.log(1)Copy the code

If we had multiple yield keywords, we would have to call the next method multiple times,

function* my() {
    yield console.log(1)
    yield console.log(2)
    yield console.log(3)
    yield console.log(4)
}
var g = my()
console.log(g.next()) // 1
console.log(g.next()) // 2
console.log(g.next()) // 3
console.log(g.next()) // 4Copy the code

If we add one more

console.log(g.next())Copy the code

So what happens at this point is,

Done then becomes true to indicate that the iteration is complete, so we can use it as an exit to the recursion

let c = g.next()
if (c.done) returnCopy the code

Encapsulate a simple Generator

1. To encapsulate, you must pass a function as an argument, so the first version of the code looks like this.

function createGen(fn) {
    var g = fn(),
        res
    function _next(val) {
        res = g.next()
        if (res.done) return res.value
        _next(res.value)
    }
    _next()
}Copy the code

2. If we run into a Promise, the wrapped function is dead, so we simply return a Promise, call then, and make a try.. Catch error handling, so the code looks like this:

function createGen(fn) {
    return new Promise((resolve, reject) => {
        var g = fn(),
            res
        function_next(val) {// Promise is an instance of a Promise, so we make a try... Catch package try {// get the object returned after the next execution, which containsdoneRes = g.next(val)} Catch (error) {reject(error)} // If the iteration is complete, then the end, which is the recursive exit we mentioned aboveif (res.done) returnResolve (res.value) // To prevent res.value from being a raw value, wrap it with a Promise and then usethenMethod processing, successful // continue to call the _next method recursion until res.done is equal tofalse
            Promise.resolve(res.value).then(
                v => _next(v),
                r => g.throw(r)
            )
        }
        _next()
    })
}Copy the code

Optimize a few small problems

  1. If the fn function is passed in with arguments
  2. This points to the question
  3. If we return a new Promise directly, it seems that we can only use the Promise in case we want to change it later, so we wrap it in an anonymous function

So my final code is:

function createGen(fn) {
    return function () {
        var self = this,
            res,
            g = fn.apply(self, arguments)
        return new Promise((resolve, reject) => {
            function_next(val) {// Promise is an instance of a Promise, so we make a try... Catch package try {// get the object returned after the next execution, which containsdoneRes = g.next(val)} Catch (error) {reject(error)} // If the iteration is complete, then the end, which is the recursive exit we mentioned aboveif (res.done) returnResolve (res.value) // To prevent res.value from being a raw value, wrap it with a Promise and then usethenMethod processing, successful // continue to call the _next method recursion until res.done is equal tofalse
                Promise.resolve(res.value).then(
                    v => _next(v),
                    r => g.throw(r)
                )
            }
            _next()
        })
    }
}Copy the code