如果await同一个 Promise 两次会怎么样?
前端小混混 前端先锋
// 每日前端夜话 第469篇
// 正文共:1000 字
// 预计阅读时间:7 分钟
先看下面这道面试题:
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 而不是它的结果。