背景
以前课上老师教这个语法的时候,会说async实际上就是promise的语法糖。后来又听某老师说async和await是generator+co的语法糖,这就让我迷糊了,于是自己探究下async和await到底是个啥。
源码
async和await是es7语法,在babel中会被转译,我们看一下专一前和转译后的源码: 转译前:
async function p ( ) {
await console. log ( 'xx' )
}
p ( )
async function w ( ) {
console. log ( 'xf' )
}
function asyncGeneratorStep ( gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[ key] ( arg) ; var value = info. value; } catch ( error ) { reject ( error) ; return ; } if ( info. done) { resolve ( value) ; } else { Promise. resolve ( value) . then ( _next, _throw) ; } }
function _asyncToGenerator ( fn) { return function ( ) { var self = this , args = arguments; return new Promise ( function ( resolve, reject) { var gen = fn. apply ( self, args) ; function _next ( value) { asyncGeneratorStep ( gen, resolve, reject, _next, _throw, "next" , value) ; } function _throw ( err) { asyncGeneratorStep ( gen, resolve, reject, _next, _throw, "throw" , err) ; } _next ( undefined) ; } ) ; } ; }
function p ( ) {
return _p. apply ( this , arguments) ;
}
function _p ( ) {
_p = _asyncToGenerator ( function * ( ) {
yield console. log ( 'xx' ) ;
} ) ;
return _p. apply ( this , arguments) ;
}
p ( ) ;
function w ( ) {
return _w. apply ( this , arguments) ;
}
function _w ( ) {
_w = _asyncToGenerator ( function * ( ) {
console. log ( 'xf' ) ;
} ) ;
return _w. apply ( this , arguments) ;
}
先解读下p函数,运行p函数就是运行_p函数,运行_p函数会返回里面的_asyncToGenerator函数。 这个函数里面包了一个function*也就是生成器函数。 再来仔细看下这个函数干了啥,这个函数返回一个函数,而这个返回的函数执行会返回一个传入的fn的返回值也就是那个生成器函数的返回值,这个生成器返回值第一次是这个生成器,第二次是它的yield语句,把返回结果做成gen,相当于:
function * myfunc ( ) {
yield console. log ( 'myfunc' )
}
gen = myfunc ( )
gen[ 'next' ] ( 参数)
然后运行它里面定义的asyncGeneratorStep函数。 这个函数就比较简单了,注意上面例子的那段话,它就是用trycatch试着运行它的next,也就是function的yield,生成器函数会有done属性,如果完成了done就是true,如果没完,把返回值利用promise.resolve包裹起来递归。 再来看一下w函数,也就是没有加上await的在function*的生成器函数里就会以正常代码存在,如果有yield穿插,看在yield的什么位置,也就是看await的位置。 最后再梳理下整个过程,运行p函数>运行_p返回内部_p运行结果——也就是个递归Promise对象,这个对象会自动执行生成器yield,直到运行完。
总结
async和await确实也算是Promise的语法糖,但实际是promise包裹了generator的语法糖。 源码中使用的特殊技巧: 这个Promise包裹的非常特别,是一个递归一直next直到没有yield才进行resolve的promise,如果下面还有yield,那么就调用Promise.resolve来递归。 关于这个和co的关系,下次整理下写出来。