Pun*_*Cha 3 javascript promise es6-promise
Code#1 和 Code#2 之间的区别是:Code#1 使用resolve(p),Code#2 使用p.then(()=>resolve())。我希望输出的序列是不变的,但它们会生成不同的序列。我不明白为什么。
resolve(p)const p = Promise.resolve();
new Promise((resolve) => {
resolve(p); // <---
}).then(() => {
console.log('after:await');
});
p.then(() => console.log('tick:a'))
.then(() => console.log('tick:b'))
.then(() => console.log('tick:c'));Run Code Online (Sandbox Code Playgroud)
输出:
tick:a
tick:b
after:await
tick:c
Run Code Online (Sandbox Code Playgroud)
p.then(()=>resolve())const p = Promise.resolve();
new Promise((resolve) => {
p.then(()=>resolve()); // <---
}).then(() => {
console.log('after:await');
});
p.then(() => console.log('tick:a'))
.then(() => console.log('tick:b'))
.then(() => console.log('tick:c'));Run Code Online (Sandbox Code Playgroud)
输出:
tick:a
after:await
tick:b
tick:c
Run Code Online (Sandbox Code Playgroud)
为什么输出的顺序不同?
这实际上是一个非常有趣的问题,因为Promise/A+ 规范将允许第一个代码版本产生与第二个代码版本相同的输出。
有人可能会驳回这个问题,因为 Promise 的实现没有说明如何resolve(p)实现。在查看 Promise/A+ 规范时,这是一个真实的陈述,引用其前言:
核心 Promises/A+ 规范不涉及如何创建、履行或拒绝 Promises,...
但是 Promise 的 EcmaScript 规范(第25.4节)比 Promise/A+ 规范更加详细,并且要求将“作业”添加到相关作业队列的后面——对于 Promise 结算来说,这是 PromiseJobs 队列( 25.4.1.3 ) .2和8.4 ):这决定了特定的顺序:
所需的作业队列
[...]
PromiseJobs:对 Promise 解决的响应的作业[...]
单个作业队列中的PendingJob记录始终按 FIFO 顺序启动
它还定义了resolve(p)——当p是 thenable 时——将首先将一个作业放入队列中,该作业将执行该p.then方法的必要内部调用。这不是立即完成的。引用 EcmaScript 规范25.4.2.2中的注释:
此过程必须作为作业进行,以确保
then在任何周围代码的评估完成后对方法进行评估。
以下代码片段中的输出顺序说明了该语句:
const p1 = Promise.resolve();
// Wrap the `p1.then` method, so we can log something to the console:
const origThen = p1.then;
p1.then = function(...args) {
console.log("The p1.then method is called asynchronously when triggered by resolve(p1)");
origThen.call(this, ...args);
};
const p2 = new Promise(resolve => {
resolve(p1);
console.log("Code that follows is executed synchronously, before p1.then is");
});Run Code Online (Sandbox Code Playgroud)
当我们使用p1.then(resolve)方法调用而不是 时resolve(p1),我们得到相反的顺序:
const p1 = Promise.resolve();
// Wrap the `p1.then` method, so we can log something to the console:
const origThen = p1.then;
p1.then = function(...args) {
console.log("The p1.then method is called synchronously now");
origThen.call(this, ...args);
};
const p2 = new Promise(resolve => {
p1.then(resolve);
console.log("Code that follows is executed synchronously, after p1.then is");
});Run Code Online (Sandbox Code Playgroud)
上面确实解释了您得到的不同输出顺序。以下是第一个代码版本如何对操作进行排序。首先让我稍微重写一下,以便大多数涉及的承诺都有一个名称:
const p1 = Promise.resolve();
const p2 = new Promise((resolve) => resolve(p1));
const p3 = p2.then(() => console.log('after:await'));
const p4 = p1.then(() => console.log('tick:a'));
const p5 = p4.then(() => console.log('tick:b'))
const p6 = p5.then(() => console.log('tick:c'));Run Code Online (Sandbox Code Playgroud)
现在,在主同步代码执行完成后,只有p1一个已解决的状态,并且作业队列(微任务队列)上存在两个作业,一个是由于以下原因,resolve(p1)另一个是因为p1.then:
根据25.4.2.2,调用then的方法p1,传递与[[resolve]]相关的内部函数p2。内部p1.then知道该问题p1已解决,并将另一个作业放入队列中以实际解决p2!
带有“tick:a”的回调被执行,promise p4 被标记为已完成,在作业队列中添加一个新作业。现在队列中有 2 个新作业,按顺序处理:
执行步骤 1 中的作业:p2 现在已解析。这意味着新作业已排队以实际调用相应的then回调
执行步骤 2 中的作业:执行带有“tick:b”的回调
只有稍后,第 3 步中添加的作业才会被执行,这将通过“after:await”调用回调。
所以,总而言之。在 EcmaScript a 中resolve(p),p其中 thenable 涉及一个异步作业,该作业本身会触发另一个异步作业来通知完成。
then区分第二个代码版本的回调只需要调用一个异步作业,因此它发生在“tick:b”输出之前。