都说async/await
是Generator
+Promise
的语法糖,经过本文逐步揭开async/await
背后的秘密...react
Generator
把异步逻辑同步化function asyncOp(x) { return new Promise((resolve, reject) => { setTimeout(() => { Number.isSafeInteger(x) ? resolve(12) : reject(new Error('Invalid integer')); }, 3000) }) } function *gen(x) { try { var y = yield asyncOp(x); return x + y; } catch(e) { console.error(e) } } var g = gen(1); // 获取异步操做 var asyncAction = g.next(); asyncAction.value .then(value => { // 把异步操做的结果值传给生成器函数 var result = g.next(value); console.log(result.value); })
总体思路:git
yield
返回异步操做(并暂停生成器函数执行);next
方法把异步操做的结果值传入生成器函数(并继续执行生成器函数);yield
表达式的值是同步仍是异步。throw
方法在暂定位置抛异常。var g = gen('a'); var asyncAction = g.next(); asyncAction.value .catch(reason => { // 经过throw告诉生成器函数异常操做发生了异常 g.throw(reason); })
function getRadom() { return (Math.random() * 100) >>> 0; } function getRandomAsync() { return new Promise(resolve => { setTimeout(() => { resolve(getRadom()); }, 2000) }) } function* sum() { var x = yield getRandomAsync(); console.log(`x=${x}`) var y = yield getRandomAsync(); console.log(`y=${y}`) return x + y; }
var gen = sum(); // 获取异步操做1 gen.next().value .then(val => { // 把异步操做1的结果传给生成器函数,并获取异步操做2 gen.next(val).value .then(val => { // 把异步操做2的结果传给生成器函数,并获最终结果 var sum = gen.next(val).value; console.log(`sum=${sum}`) }) })
上面的写法就像记流水帐,若是有3个数相加还这样写岂不是要疯。github
// 生成器函数执行器 function runner(genFunc) { // 建立生成器对象 var gen = genFunc(); // 开启执行 return doRun(); function doRun(arg) { // 把上一个异步操做结果`arg`传如生成器函数 var data = gen.next(arg); if(data.done) { return Promise.resolve(data.value); } // 若是还没结束,就等异步操做结束后递归调用doRun。 return Promise.resolve(data.value).then(doRun); } } runner(sum).then(sum => { console.log(sum) })
注意:
这里使用Promise.resolve
方法把data.value
转成Promise
,由于Promise.resolve
的特殊功能:若是实参value是个Promise对象,则直接返回实参babel
function runner(genFunc) { var gen = genFunc(); return new Promise((resolve, reject) => { doRun(); function doRun(arg) { try { // 捕获`next`方法抛出的异常 var data = gen.next(arg); if(data.done) { return resolve(data.value); } // 若是还没结束,就等异步操做结束后递归调用doRun。 return Promise.resolve(data.value) .then(doRun) .catch(gen.throw) // 经过`throw`方法告诉生成器异常了 .catch(reject) // 捕获`throw`方法抛出的异常(即生成器方法没有捕获处理异常) } catch(error) { reject(error); } } }) } runner(sum).then(sum => { console.log(sum) }) .catch(reason => { console.error(reason) })
throw
方法,next
方法也可能会抛出异常,因此在最外层使用try-catch
捕获next
方法抛出的异常;runner
方法的返回值也再也不是doRun()
了,而改为了Promise
,用于处理next
方法抛出的异常和最终的结果值。捕获throw
方法抛出的异常也能够采用try-catch
方式,这样就跟捕获next
方法抛出的异常保持一致了:app
function runner(genFunc) { var gen = genFunc(); return doRun(); function doRun(arg) { return new Promise((resolve, reject) => { step('next'); function step(methodName, arg) { try { // 捕获`next`方法抛出的异常 var data = gen[methodName](arg); } catch(error) { reject(error); return; } if(data.done) { return resolve(data.value); } return Promise.resolve(data.value) .then(value => { step('next', value); }) .catch(reason => { step('throw', reason); }) } }) } } runner(sum) .then(sum => { console.log(sum) }) .catch(reason => { console.error(reason) })
async/await
的Generator
+Promise
写法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); } } // 负责把`async`转成`generator` function _asyncToGenerator(fn) { return function () { // 处理传给生成器的参数 var self = this, args = arguments; return new Promise(function (resolve, reject){ // 生成器的函数在Promise参数的回调函数里执行,而且处理参数 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); }); }; }
使用固定的模式把async
转成Generator
,Babel的实现更严谨些:异步
Generator
,调用时再传参。参考这个调整以前的实现:async
function _asyncToGenerator(genFunc) { return function() { var args = arguments; var self = this; return new Promise((resolve, reject) => { var gen = genFunc.apply(self, args); step('next'); function step(methodName, arg) { try { var data = gen[methodName](arg); } catch(error) { reject(error); return; } if(data.done) { return resolve(data.value); } return Promise.resolve(data.value) .then(value => { step('next', value); }) .catch(reason => { step('throw', reason); }) } }) } } _asyncToGenerator(sum)() .then(sum => { console.log(sum) }) .catch(reason => { console.error(reason) })
总结下async/await
转成Genertor
方式:函数
await
直接替换成yield
;async
函数体代码转成生成器代码(匿名的生成器函数);async
函数名被转成普通函数内部调用生成器函数的函数。async function sum() { var x = await 1; var y = await 2; return x + y; } // 对应的生成器方式 function sum() { return _sum.apply(this, arguments); } function _sum() { // 注意:函数体内的_sum变量是个局部变量,不影响外部做用域下的_sum的值。 _sum = _asyncToGenerator(function* () { var x = yield 1; var y = yield 2; return x + y; }); // 建立生成器对象,并开始执行生成器 return _sum.apply(this, arguments); }