先看下面这道面试题:
let counter = 0;
const increment = new Promise(resolve => {
counter++;
resolve();
});
await increment;
await increment;
console.log(counter); // 结果是什么?
你认为 counter
的值是什么?
什么是 promise ?这是否意味着“稍后再做”?
实际上应该把 promise 看成是一个状态机。
promise 以“pending”状态开始它的生命周期。如果你要在这个状态下查询结果,则必须排队。
根据 ECMAScript 标准文档中的描述(https://tc39.es/ecma262/),上面 Promise
构造函数会立即调用我们的执行器函数。它的 counter++
副作用运行。不带任何参数调用resolve()
,在 promise 上存储一个 undefined
结果,并将其状态提升为“fulfilled”。
第一个 await
运行。 await
等同于在你的 promise 上运行.then(onFulfilled)
,将 onFulfilled
设置为“前面的代码”。这项工作作为微任务进入队列。 JavaScript 以先进先出的顺序执行微任务;控制最终返回给我们的函数。
第二个 await
没有什么不一样的地方。它创建一个微任务给我 promise 的结果并提前运行代码,然后等待 JavaScript 进行调度。
副作用只在 Promise
构建期间运行过一次,所以 counter
为 1
.
我们推迟了工作吗?没有。promise 是同步创建和执行的。但是我们使用了 await
来处理其他微任务。
那么应该怎样推迟工作?
const microtask = Promise.resolve()
.then(() => console.log('hello from the microtask queue'));
const macrotask = new Promise(resolve =>
// 排队宏任务
setTimeout(() => {
console.log('hello from the macrotask queue');
resolve();
})
// 没有解决,promise 仍处于 pending 状态
);
// 这是最先输出的; 我们成功地推迟了工作
console.log('hello from the original execution context');
// yield 调度器; 运行 .then() 并输出日志
await microtask;
// yield 调度器; promise 的状态是 fulfilled ,所以没有日志输出
await microtask;
// yield 调度器; 执行所有微任务,然后运行宏任务并输出日志
await macrotask;
// yield 调度器; promise 已经完成,所以没有日志输出
await macrotask;
Promise
构造函数是同步运行的,但是我们不必同步调用 resolve()
。 Promise.prototype.then
也推迟了工作。
Promise
构造函数和 Promise.prototype.then
都不会重复工作。
这意味着 promise 可以用来记住异步计算。如果你用了一个 promise,而且想要稍后再次用到它的结果,应该考虑保留 promise 而不是它的结果。
本文首发微信公众号:前端先锋
欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章
欢迎继续阅读本专栏其它高赞文章:
- 深入理解Shadow DOM v1
- 一步步教你用 WebVR 实现虚拟现实游戏
- 13个帮你提高开发效率的现代CSS框架
- 快速上手BootstrapVue
- JavaScript引擎是如何工作的?从调用栈到Promise你需要知道的一切
- WebSocket实战:在 Node 和 React 之间进行实时通信
- 关于 Git 的 20 个面试题
- 深入解析 Node.js 的 console.log
- Node.js 究竟是什么?
- 30分钟用Node.js构建一个API服务器
- Javascript的对象拷贝
- 程序员30岁前月薪达不到30K,该何去何从
- 14个最好的 JavaScript 数据可视化库
- 8 个给前端的顶级 VS Code 扩展插件
- Node.js 多线程完全指南
- 把HTML转成PDF的4个方案及实现
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。