预备知识
- Promise
- Generator
- 生成器函数
- async函数
- 选择正确的JavaScript异步方法
async/await 规则
- await 必须出现在 async 声明的函数后面
- await 后面的值如果是一个 Promise,await 将等待 Promise 正常处理完成并返回其处理结果
- await 后面的值如果不是一个 Promise,await 会把该值转换为已正常处理的Promise,然后等待其处理结果
- await 后面的 Promise 的状态 pending -> fulfilled 后, await 后面的代码会放在微任务队列中
Generator 函数和自动执行器表示 async/await
async函数文档中以后这样一句话:
这里的“好像搭配使用了生成器和promise”怎么理解呢?我在网上找到了下面这段代码,很好的解释了这里的“搭配使用了生成器和promise”
async function fn(args){
// await ...
// await ...
// await ...
}
// 等同于
function fn(args){
function spawn(genF) {
return new Promise(function(resolve, reject) {
var gen = genF();
function step(nextF) {
try {
var next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() {
return gen.next(v);
});
}, function(e) {
step(function() {
return gen.throw(e);
});
});
}
step(function() {
return gen.next(undefined);
});
});
}
return spawn(function*() {
// yield ...
// yield ...
// yield ...
});
}
fn()
这段代码的思路是:将Generator 函数和自动执行器,包装在一个函数里。
- fn 函数返回一个新的函数 spawn 的执行结果;
- spawn 函数参数是一个 生成器函数
- spawn 返回一个 Promise, 对应与 async 函数返回一个 Promise;
let hello = async () => { return "Hello" };
hello().then((value) => console.log(value))
// output: Hello
let helloWorld = async () => {
await "Hello"
await "world"
return "Nick"
};
helloWorld().then((value) => console.log(value))
// output: Nick
- spawn 内部创建了一个生成器
var gen = genF();
- spawn 会执行 step 函数, 参数是一个 function, nextF;
- 首次执行, next(undefined), 表示不向生成器传值
- 如果生成器没有执行结束, step 会把当前生成器产生的值, 即 yield 的返回值通过 Promise.resolve 封装为 Promise
- 如果 yield 的返回值是Promise,返回原Promise
- 如果这个值是thenable(即带有"then" 方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态
- 否则返回的promise将以此值完成
- 当 Promise 为完成状态时,会把 yield 后面的代码放在微任务队列
- 当执行到该微任务时, 再次执行next方法, 把上次产生的值放传递给生成器
- 循环执行, 直到生成器结束
这样就用 Generator 函数和自动执行器产生了 async/await 的执行效果, 所以也可以说 async/await 是 Generator 的语法糖
源码解析
AST spec
interface Function <: Node {
id: Identifier | null;
params: [ Pattern ];
body: BlockStatement;
generator: boolean;
async: boolean;
}
插件代码
export default declare((api, options) => {
api.assertVersion(7);
const { method, module } = options;
if (method && module) {
return {
name: "transform-async-to-generator",
visitor: {
Function(path, state) {
if (!path.node.async || path.node.generator) return;
let wrapAsync = state.methodWrapper;
if (wrapAsync) {
wrapAsync = t.cloneNode(wrapAsync);
} else {
wrapAsync = state.methodWrapper = addNamed(path, method, module);
}
remapAsyncToGenerator(path, { wrapAsync });
},
},
};
}
return {
name: "transform-async-to-generator",
visitor: {
Function(path, state) {
if (!path.node.async || path.node.generator) return;
remapAsyncToGenerator(path, {
wrapAsync: state.addHelper("asyncToGenerator"),
});
},
},
};
});
// remapAsyncToGenerator 部分代码
export default function (
path: NodePath,
helpers: { wrapAsync: Object, wrapAwait: Object },
) {
path.traverse(awaitVisitor, {
wrapAwait: helpers.wrapAwait,
});
const isIIFE = checkIsIIFE(path);
path.node.async = false;
path.node.generator = true;
wrapFunction(path, t.cloneNode(helpers.wrapAsync));
const isProperty =
path.isObjectMethod() ||
path.isClassMethod() ||
path.parentPath.isObjectProperty() ||
path.parentPath.isClassProperty();
if (!isProperty && !isIIFE && path.isExpression()) {
annotateAsPure(path);
}
}
代码主要工作先通过 !path.node.async || path.node.generator
判断如果不是 async 声明的函数或者是 generator 函数则返回.
否则调用 babel-helper-remap-async-to-generator
将 async 转为 generator, 如果是立即执行函数, 会有额外的处理.
UT
// input
(async function() { await 'ok' })();
(async function notIIFE() { await 'ok' });
// output
babelHelpers.asyncToGenerator(function* () {
yield 'ok';
})();
/*#__PURE__*/
(function () {
var _notIIFE = babelHelpers.asyncToGenerator(function* () {
yield 'ok';
});
function notIIFE() {
return _notIIFE.apply(this, arguments);
}
return notIIFE;
})();
从 UT 可以看出 async/await 被转成了 babelHelpers.asyncToGenerator
再看一下 asyncToGenerator 长什么样子:
helpers.asyncToGenerator = helper("7.0.0-beta.0")`
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);
}
}
export default 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);
});
};
}
`;
和上面 Generator 函数和自动执行器表示 async/await的代码如出一辙.
小结
async/await 的目的为了简化使用基于promise的API时所需的语法, 可以说是 Generator 的语法糖。 babel 通过 plugin-transform-async-to-generator 将 async/await 转为 Generator和自动执行器的方式。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!