async、await - generator的语法糖

110 阅读2分钟

1、async await由来

众所周知,async以及await的出现只要是为了解决promise以及xhr等回调地狱的问题的,使用的办法就是相当于平时的同步函数一样,要点:

  1. await必须用在async函数里面
  2. 返回值是一个promise(fulfilled或者rejected)

2、generator函数

以下是一个典型的generator函数,函数名前有个*号,里面的yield归还控制权 给主程序,这就是js里面的协程。从中可以看到两点

  1. 每次调用next函数,则会返回一个对象,对象有两个属性value以及done,value是每个yield后面的返回值,done代表该函数是否已经执行结束了。
  2. 每次在next函数里面传入的值会被对应的前一个yield接收,比如里面的a,b
function * myGenerator(){
    const a = yield 1;
    const b = yield 2;
    console.log("my a:",a)
    // my a: a
    console.log("my b:",b)
    // my b: b
    return "over"
}

let myGen = myGenerator()
console.log(myGen.next())
// { value: 1, done: false }
console.log(myGen.next("a"))
// { value: 2, done: false }
console.log(myGen.next("b"))
// { value: 'over', done: true }

3、async\await - generator的语法糖

在generator函数中,yield会让出控制权,然后后面的代码将会不会执行,直到下一次调用next交还控制权才会继续执行,那么这个场景是否有见过呢? 由此可以想到,在我们的async函数中await后面返回的promise变为rejected或者fulfilled才能继续执行下面的函数,那么这两种情况一对比,那么就可以得出:如果要是想实现async\await,就得在promise.then或者promise.catch里面继续调用next函数,调用next可以交还控制权继续执行后面的代码

4、async函数的实现就是将Generator函数和自动执行器包装在一个函数中,这个自动执行器实现如下:(抄载于阮一峰)

/**
 * 
 * @param {*} genF 一个gerneator函数
 * @returns 一个promise对象
 */
function spawn(genF){
    // 每个async函数的返回值都是一个promise对象
    return new Promise(function(resolve,reject){
        var gen = genF();
        function step(nextF){
            // 1、获取第一个yield的返回值
            try{
                var next = nextF()
            }catch(e){
                return reject(e)
            }
            // 如果已经结束,那么就直接resolve该promise
            if(next.done){
                return resolve(next.value)
            }
            // next.value代表指的是上一个yield的返回值,then是代表next.value代表promise已经改变了状态
            // 也就是说在这个返回值完成之后再调用next归还执行权给他,运行下一步代码
            Promise.resolve(next.value).then(
                function(v){
                    step(function(){return gen.next(v)})
                },
                function(err){
                    // 抛出错误,第一个try那里接收
                    step(function(){return gen.throw(err)})
                }
            )  
        }
        step(function(){gen.next(undefined)})
    })
}