为什么要用p.then(resolve)早于resolve(p)来解决新的promise? [英] Why is a new promise resolved earlier with p.then(resolve) than with resolve(p)?

查看:64
本文介绍了为什么要用p.then(resolve)早于resolve(p)来解决新的promise?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

代码#1和代码#2之间的区别是:代码#1使用 resolve(p),代码#2使用 p。 (()=> resolve())。我希望输出的序列是不变的,但是它们会生成不同的序列。我不知道为什么。



代码#1: 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'));  



输出:

 刻度:a 
刻度:b
after:await
刻度:c



代码#2: 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'));  



输出:



< pre class = lang-none prettyprint-override> 刻度:a
after:await
刻度:b
刻度:c

为什么输出顺序不同?

解决方案

这实际上是一个非常有趣的问题,因为 Promise / A +规范将允许第一个代码版本产生与第二版代码相同的输出。


一个人可以忽略以下问题:Promise实现对 resolve(p)将被实施。当看Promise / A +规范时,这是一个真实的声明,引自其序言:


Promises / A +核心规范不涉及如何创建,履行或拒绝承诺,...


但是EcmaScript的Promises规范(第 25.4 )比Promise / A +规范更为详细,并要求职位被添加到相关作业队列的后面-对于PromiseJobs 队列( 25.4.1.3.2 8.4 ):这确定了特定的顺序:


所需的作业队列


[...]

PromiseJobs :响应承诺的解决的工作


[...]


单个作业队列中的 PendingJob 记录始终以FIFO顺序启动


它还定义了 resolve(p)-当 p 是thenable-将 first 放在队列中,该队列将执行 p.then 方法的必要内部调用。这立即完成。在 25.4.2.2 :


此过程必须作为作业进行,以确保对 then 方法的求值


此语句在以下代码段中以输出顺序说明:


  const p1 = Promise.resolve(); 

//包装`p1.then`方法,因此我们可以将某些内容记录到控制台:
const origThen = p1.then;
p1.then = function(... args){
console.log( p1.then方法在由resolve(p1)触发时被异步调用。);
origThen.call(this,... args);
};
const p2 = new Promise(resolve => {
resolve(p1);
console.log(后面的代码在p1.then之前是同步执行的);
});


当我们使用 p1时。然后调用(resolve)方法而不是 resolve(p1),我们得到相反的顺序:


  const p1 = Promise.resolve(); 

//包装`p1.then`方法,因此我们可以将某些内容记录到控制台:
const origThen = p1.then;
p1.then = function(... args){
console.log( p1.then方法现在被同步调用);
origThen.call(this,... args);
};
const p2 = new Promise(resolve => {
p1.then(resolve);
console.log(后面的代码在p1.then之后是同步执行的);
});


您的代码


以上内容确实说明了您获得的不同输出顺序。这是第一个代码版本如何对动作进行排序的方式。首先让我重写一下,以便大多数涉及的Promise都有一个名称:


  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'));


现在,在执行主要的同步代码后, ,只有 p1 具有已解决状态,并且在作业队列(微任务队列)中存在两个作业,一个是 resolve(p1 )和第二个,原因是 p1。然后


  1. 根据 25.4.2.2
    调用 p1 then 方法,将其传递给内部 [[resolve]] 函数与 p2 有关。 p1.then 内部人员知道 p1 已解决,并将另一个作业放在队列中以实际解决 p2



  2. 带有 tick:a的回调执行后,将promise p4标记为已完成,从而在作业队列中添加一个新作业。
    队列中现在有2个新作业,这些作业按顺序处理:



  3. 执行步骤1中的作业: p2现在已解决。这意味着新作业已排队等待实际调用相应的然后回调



  4. 执行来自步骤2的作业:使用 tick:b的回调被执行



仅稍后执行步骤3中添加的作业,该作业将使用 :await。


因此,总而言之。在EcmaScript中, resolve(p),其中 p 是可行的,它涉及一个异步作业,该作业本身会触发另一个异步


然后 then 回调(用于区分第二个代码版本)只需要一个 异步作业被调用,因此它发生在 tick:b输出之前。


The difference between Code#1 and Code#2 is: Code#1 uses resolve(p) and Code#2 uses p.then(()=>resolve()). I would expect the sequence of output to be invariant, but they generate a different sequence. I cannot figure out why.

Code #1: 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'));

Output:

tick:a
tick:b
after:await
tick:c

Code #2: 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'));

Output:

tick:a
after:await
tick:b
tick:c

Why is the order of output different?

解决方案

This is in fact a very interesting question, because the Promise/A+ specs would allow the first code version to produce the same output as the second version of the code.

One could dismiss the question saying the Promise implementation says nothing about how resolve(p) would be implemented. This is a true statement when looking at the Promise/A+ specification, quoting from its preface:

the core Promises/A+ specification does not deal with how to create, fulfill, or reject promises, ...

But the EcmaScript specification for Promises (Section 25.4) is quite more detailed than the Promise/A+ specification and requires that "jobs" are added to the back of the relevant job queue -- which for promise settlements is the PromiseJobs queue (25.4.1.3.2 and 8.4): this determines a specific order:

Required Job Queues

[...]
PromiseJobs: Jobs that are responses to the settlement of a Promise

[...]

The PendingJob records from a single Job Queue are always initiated in FIFO order

It also defines that resolve(p) -- when p is a thenable -- will first put a job on the queue that will perform the necessary internal call of the p.then method. This is not done immediately. To quote the note in the EcmaScript specs at 25.4.2.2:

This process must take place as a Job to ensure that the evaluation of the then method occurs after evaluation of any surrounding code has completed.

This statement is illustrated with the order of output in the following snippet:

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");
});

When we use the p1.then(resolve) method call instead of resolve(p1), we get the opposite order:

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");
});

Your code

The above really explains the different order of output you get. Here is how the first code version sequences the actions. First let me rewrite this a bit, so that most involved promises have a name:

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'));

Now, after the main, synchronous code has executed to completion, only p1 has a resolved state, and two jobs are present on the job queue (micro tasks queue), one as a result of resolve(p1) and a second one because of p1.then:

  1. According to 25.4.2.2, the then method of p1 is called passing it the internal [[resolve]] function related to p2. The p1.then internals know that p1 is resolved and put yet another job on the queue to actually resolve p2!

  2. The callback with "tick:a" is executed, and promise p4 is marked as fulfilled, adding a new job in the job queue. There are now 2 new jobs in the queue, which are processed in sequence:

  3. The job from step 1 is executed: p2 is now resolved. This means a new job is queued to actually call the corresponding then callback(s)

  4. The job from step 2 is executed: the callback with "tick:b" is executed

Only later the job added in step 3 will be executed, which will call the callback with "after:await".

So, in conclusion. In EcmaScript a resolve(p), where p is a thenable involves an asynchronous job, which itself triggers yet another asynchronous job to notify the fulfilment.

The then callback, that differentiates the second code version, will only need one asynchronous job to get called, and thus it happens before the output of "tick:b".

这篇关于为什么要用p.then(resolve)早于resolve(p)来解决新的promise?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆