承诺输出在NodeJs和Browser之间是不同的

Promise output varies between NodeJs and Browser

本文关键字:之间 是不同 Browser 输出 NodeJs 承诺      更新时间:2023-09-26

以下代码块在Node和Browser之间的执行方式不同。当然,存在不同的环境、不同的处理时间和竞争条件的可能性。但是根据我对Promises的理解,这些代码应该在不同的环境中保持一致。

我期待Chrome/浏览器的结果。我不期望NodeJs的结果。我不明白为什么每个newPromisethen链在 masterPromisethen链继续之前没有完成。换句话说,因为一个新的Promise在随后的fn中被返回到masterPromise Promise -chain,我希望新Promise的then-chain在masterPromise Promise -chain恢复之前完成。

我希望有人能在下面的实现中戳一个洞,并解释为什么NodeJs结果是有效的!

使用chrome 44和node 12.6.

,

'use strict';
var masterPromise = Promise.resolve();
var numbers = [ 1, 2, 3 ];
// function returns a new promise that fulfills in 100ms
// it logs two bits of information--one pre-resolve, & one post-resolve.
// because a `.then` is registered immediately, before the promised is
// fulfilled, i would expect the post-resolve console.log to be logged before
// any other logging
var returnNewPromise = function(number) {
    var resolve;
    var newPromise = new Promise(function(r) { resolve = r; });
    newPromise.then(function() { console.log('registered ' + number + ' (verbatim, syncronous echo)'); });
    setTimeout(function() {
        console.log('registered ' + number);
        resolve();
    }, 100);
    return newPromise;
};
numbers.forEach(function(number) {
    var getChildPromise = function(number) {
        return returnNewPromise(number);
    };
    return masterPromise.then(function() {
        return getChildPromise(number);
    });
});
节点:

registered 1
registered 2
registered 3
registered 1 (verbatim, syncronous echo)
registered 2 (verbatim, syncronous echo)
registered 3 (verbatim, syncronous echo)
Chrome:

registered 1
registered 1 (verbatim, syncronous echo)
registered 2
registered 2 (verbatim, syncronous echo)
registered 3
registered 3 (verbatim, syncronous echo)

我不明白为什么每个newPromisethen链不是在之前完成masterPromisethen链继续。一个新的Promise在then中返回到masterPromise Promise -chainfn,所以它不应该在masterPromise之前等待promise-chain简历吗?

。看来你的误解是关于"masterPromise链":没有这样的东西。
您有一个masterPromise,然后您从它链接了三个不同的then调用。当masterPromise解析时(这是立即的),它看到3个回调,并按照它们注册的顺序调用它们。它不关心这些回调做什么,它们是否是异步的,也不等待它们的承诺结果。在你的例子中,它们都创建了承诺,并通过它们推进它们的子链,但这3个子链是完全独立的。

也许用更多的描述性日志来充实你的代码有助于理解你在做什么:

function delay(number) {
    console.log("creating promise for", number);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log('resolving promise with', number);
            resolve(number);
        }, 100);
    });
};
function result(n) {
    console.log("received", number);
}
var masterPromise = Promise.resolve();
masterPromise.then(function(){ delay(1).then(result); }); // chain 1
masterPromise.then(function(){ delay(2).then(result); }); // chain 2
masterPromise.then(function(){ delay(3).then(result); }); // chain 3
console.log("created chains");

你将在这里看到的日志是

// .then chain 1    -------,
// .then chain 2    ------- ' -,
// .then chain 3    -------- ' -' -,
created chains                |  |  |
                              |  |  | 3 then callbacks in the order they were registered
creating promise for 1      <´   |  |
// setTimeout 100   -----,       |  |
                          '     /   |
creating promise for 2     | <-´    |
// setTimeout 100   ------ |-,      /
                           |  '    /
creating promise for 3     |  | <-´
// setTimeout 100   ------ |- |-,
                           |  |  '
…                          |  |  | 3 timeout callbacks in the order they were scheduled
                           |  |  |
resolving promise with 1 <´   |  |
// resolve()                  |  |
[…]                           /  |
resolving promise with 2  <-´    |
// resolve()                     /
[…]                             /
resolving promise with 3   <---´
// resolve()
[…]

这里我们可以看到3个(独立的)resolve()调用正在发生。他们将尽快安排各自的回调(result) 。这里是node和Chrome之间的区别:前者在相同的tick中执行相同时间的超时回调(它们被安排在相同的超时时间),而Chrome使用单独的tick。所以在节点中,"asap"是在三个回调之后,而在Chrome中它是在它们之间。

我不明白为什么每个newPromise then链在解析时没有立即执行

then-回调不立即调用。

var resolve;
new Promise(function (r) { resolve = r; })
    .then(function () { console.log(2); });
resolve();
console.log(1);
日志:

1
2

承诺解析与setTimeout一样,具有零延迟。

更新:

setTimeout的比较不太正确,因为有局限性:https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout#Minimummaximum_delay_and_timeout_nesting相似之处在于异步类型的承诺。

更新2:

-----------+------------
wait       |  resolved
-----------+------------
timeout1   |             initial state
timeout2   |
-----------+------------
           | timeout1    after 100 ms
timeout2   |
-----------+------------
           | resolve1    then Chrome variant
           | timeout2
-----------+------------
           | timeout2    or Node variant
           | resolve1
-----------+------------

两个变体都符合规范。Chrome队列似乎在随后的超时回调之前解决回调。