JavaScript 承诺 then() 排序

JavaScript Promise then() ordering

我还在学习 JavaScript Promises,我遇到了一个我不明白的行为。

var o = $("#output");
var w = function(s) {
    o.append(s + "<br />");
}

var p = Promise.resolve().then(function() {
    w(0);
}).then(function() {
    w(1);
});

p.then(function() {
    w(2);
    return new Promise(function(r) {
        w(3);
        r();
    }).then(function() {
        w(4);
    });
}).then(function() {
    w(5);
});

p.then(function() {
    w(6);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>

我希望这些语句按顺序 运行——也就是说,输出将是

0
1
2
3
4
5
6

相反,输出是

0
1
2
3
6
4
5

即使删除内部 Promise 也会给出在我看来矛盾的结果。 12之前输出,但是65之前输出。

有人能给我解释一下吗?

我注意到,每次重新分配 p 都会得到我期望的顺序。

r() 是做什么的?

顺序是不确定的,因为你在做同样的承诺 -> 这特指第二和第三条链。

如果你是做以下的,那么顺序是可以保证的:

var p = Promise.resolve().then(function() {
    w(0);
}).then(function() {
    w(1);
});

// Key difference, continuing the promise chain "correctly".
p = p.then(function() {
    w(2);
    return new Promise(function(r) {
        w(3);
        r();
    }).then(function() {
        w(4);
    });
}).then(function() {
  w(5);
});

p.then(function() {
  w(6);
});

你早早看到 6 的原因是你没有链接,而是分支。

当您调用 p.then().then().then() 时,您得到了 必须 以正确顺序执行的承诺链。
但是,如果您调用 p.then().then(); p.then(),您会在 p 上附加 2 个承诺 - 本质上是创建一个分支,第二个分支将与第一个分支一起执行。

您可以通过确保将它们链接在一起来解决此问题 p = p.then().then(); p.then();

仅供参考,您几乎从不想分支,除非您将它们重新组合在一起(例如 Promise.all),或者有意创建一个 "fire and forget" 分支。

为了清楚起见,让我们为示例中的每个承诺和功能命名:

var pz = Promise.resolve();
function f0() { w(0); }
var p0 = pz.then(f0);
function f1() { w(1); }
var p1 = p0.then(f1);  // p1 is 'p' in your example

function f2() {
    w(2);
    function f3(resolve_p3) {
        w(3);
        resolve_p3();
    }
    var p3 = new Promise(f3);
    function f4() { w(4); }
    var p4 = p3.then(f4);
    return p4;
}
var p2 = p1.then(f2);
function f5() { w(5); }
var p5 = p2.then(f5);

function f6() { w(6); }
var p6 = p1.then(f6);

让我们一步步看看会发生什么。首先是顶层执行:

  • pz满足,因此pz.then(f0)立即排队f0执行,其结果将解析p0
  • f1预定在p0完成后排队,其结果将解析p1.
  • f2预定在p1完成后排队,其结果将解析p2.
  • f5预定在p2完成后排队,其结果将解析p5.
  • f6预定在p1完成后排队,其结果将解析p6.

然后排队的作业(最初只有 f0)将是 运行:

  • f0 被执行:打印“0”。 p0 已完成,因此 f1 被添加到队列中。
  • f1 被执行:打印“1”。 p1 已满,因此 f2f6 被添加到队列中(按此顺序)。这是关键的一点,因为它意味着 f6 将在任何稍后排队的作业之前执行。
  • f2 被执行:打印“2”。
  • (在 f2 内):new Promise 调用 f3,它打印“3”并满足 p3.
  • (inside f2):由于p3已经满足,所以将f4加入队列,其结果将解析p4.
  • f2最终将p2解析为p4,也就是说一旦p4满足,p2.
  • 也会满足
  • f6 被执行:打印“6”。 p6 圆满了。
  • f4 被执行:打印“4”。 p4 变得圆满了。 p2 已完成,因此 f5 被添加到队列中。
  • f5 被执行:打印“5”。 p5 圆满了。