一道打印顺序引发的血案
异步编程是 JS 中重要的模块,前几日逛 B 站,刷到这么一个提问,拿来研究一下:
let promise1 = Promise.resolve()
.then(res => console.log(1))
.then(res => console.log(2))
let promise2 = new Promise(resolve => {
setTimeout(() => {
console.log(6)
resolve()
})
}).then(res => console.log(3))
async function main() {
console.log(4)
console.log(await Promise.all([promise2, promise1]))
console.log(5)
return { obj: 5 }
}
let promise3 = Promise.resolve()
.then(res => console.log(8))
.then(res => console.log(9))
console.log(typeof main())
变量对象声明
首先会创建变量对象,声明函数和变量,函数的声明优先级比变量高。会变成
VO = {
main: reference async function(){},
promise1: undefined,
promise2: undefined,
promise3: undefined,
}
接下来 代码执行阶段,首先会对变量进行赋值:
对 promise1 来说:Promise.resolve()
会立即执行then(res => console.log(1)).then...
会放入本轮的微任务
对 promise2 来说
开启一个 setTimeout 宏任务,之后的 then 会在宏任务结束后的 当次 微任务 中执行
对 promise3 来说:Promise.resolve()
会立即执行then(res => console.log(8)).then...
会放入本轮的微任务
进入 main 函数:
执行 console.log(4)
执行 console.log(await Promise.all([promise2, promise1]))
这一步是整个函数最关键的地方。
async...await 执行顺序
async...await 是 generator 的语法糖。本质是 generator + co 库,内置函数执行器。会自动执行每一个 yeild,把最后执行的结果通过 Promise resolve 的方式抛出。方便链式调用。
我们把代码通过 babel 转译,可以看下面代码中注释的 1 、2 两个地方。
"use strict";
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;
// 2. 执行 async 首先返回一个 promise
// 返回之前会先执行完 Promise 里面的 executor 函数
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);
});
};
}
var promise1 = Promise.resolve().then(function (res) {
return console.log(1);
}).then(function (res) {
return console.log(2);
});
var promise2 = new Promise(function (resolve) {
setTimeout(function () {
console.log(6);
resolve();
});
}).then(function (res) {
return console.log(3);
});
function main() {
return _main.apply(this, arguments);
}
function _main() {
// 1. main 函数开始真正执行的是 _asyncToGenerator
_main = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
// 3. regeneratorRuntime 函数来自 facebook 的 regenerator 模块,用来编译 generator 函数
// mark 函数为 目标函数的原型链上注入了 next、throw、return 等函数
// wrap 函数包装一层,把next 的调用指向外部的 context
// 这一步核心可以看 regenerator 源码
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
console.log(4);
_context.t0 = console;
_context.next = 4;
return Promise.all([promise2, promise1]);
case 4:
_context.t1 = _context.sent;
_context.t0.log.call(_context.t0, _context.t1);
console.log(5);
return _context.abrupt("return", {
obj: 5
});
case 8:
case "end":
return _context.stop();
}
}
}, _callee);
}));
return _main.apply(this, arguments);
}
var promise3 = Promise.resolve().then(function (res) {
return console.log(8);
}).then(function (res) {
return console.log(9);
});
console.log(typeof(main()));
等待 console.log(await Promise.all([promise2, promise1]))
执行完,这一行比较有迷惑性,promise1 和 promise2 的执行前面已经触发,此处只是等他们的异步全部执行完。
之后的执行就是同步操作了
自此完整输出顺序为:
4
'object' //async 函数返回
1
8
2
9
6
3
[undefined, undefined]
5
regenerator
关于 regenerator 模块的参考资料,感兴趣的可以了解以下资料
1、https://www.iteye.com/blog/schifred-2369320
2、https://juejin.cn/post/6844903701908291592
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。