Open
Description
先来看题吧,以下代码执行后的输出是什么呢?
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve(4);
}).then((res) => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() =>{
console.log(6);
})
浏览器中输出为「0 1 2 3 4 5 6」, 你可能会感觉有点奇怪,4为什么会跑到3之后才输出呢?
如果说需要等待返回的 Promise.resolve(4) 状态变更, 那也只要等待一个 tick,也就是创建一个微任务去处理就行了,最终输出「0 1 2 4 3 5 6」。这正是使用我们之前根据 Promise/A+ 规范手写的 MyPromise 会输出的结果。
之前的代码如下:
// 如果返回的是一个 Promise 对象
if (typeof then === "function") {
// 让then执行,第一个参数是this,后面是成功的回调 和 失败的回调
then.call(
x,
(res) => {
// 成功和失败只能调用一个
if (called) return;
called = true;
// 递归解析的过程(因为可能 promise 中还有 promise)
resolvePromise(promise2, res, resolve, reject);
},
(err) => {
// 成功和失败只能调用一个
if (called) return;
called = true;
reject(err); // 失败了就失败了
}
);
}
但实际在浏览器中的表现是创建了两个微任务导致 4 会延迟两个 tick 输出。所以问题来了:为什么之前编写的通过所有 Promise/A+ 测试用例的代码,执行结果会和原生 Promise 不一样?
这个就得从 ECMA 规范中解释了:
- 在 ECMA 规范中,promise.then()中的 onFullFilled 和 onRejected 两个回调函数,会在 promise 状态不为 pending 时被加入到微任务队列,该微任务在规范中被称为 PromiseReactionJob。
- 如果 A.then() 的回调函数返回值是一个 Promise 对象(命名为B),那么会生成一个微任务。这个微任务的内容是调用A.then(resolvePromise1, rejectPromise1),将 promiseA 和 promiseB 关联起来。简单来说就是 A 需要等待 B 的状态转换,所以多用了一个 tick 将 A 的状态 bind 到 B上。这就是原生 Promise 多创建的一个微任务。
Promise/A+ 规范似乎并没有对这个规范做要求,依据 A+ 规范实际使用中好像也没什么副作用影响,感觉两次微任务没什么必要性,目前的 Promise 日常操作,一次微任务都是可以满足的。
Metadata
Metadata
Assignees
Labels
No labels