承诺链是否保证只有在前一个承诺完成后才会调用下一个承诺的回调?

Tur*_*lov 1 javascript asynchronous promise

在js中遇到过一篇关于promises的文章,作者展示了一段代码:

// I want to remove() all docs
db.allDocs({include_docs: true}).then(function (result) {
  result.rows.forEach(function (row) {
    db.remove(row.doc);  
  });
}).then(function () {
  // I naively believe all docs have been removed() now!
});
Run Code Online (Sandbox Code Playgroud)

并说

这段代码有什么问题?问题是第一个函数实际上返回的是 undefined,这意味着第二个函数不会等待 db.remove() 在所有文档上被调用。事实上,它不等待任何东西,并且可以在删除任意数量的文档时执行!

所以,据我所知,如果第二个回调不function() {}接受任何参数,它实际上不会等待回调结束function(result)。我推断正确吗?因为当我运行以下代码时,它给了我相反的结果:

var array = [];
for ( let i = 0; i < 1000; i++ )
    array.push(i);

var promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve(), 1000);
});

promise
.then(() => array.forEach(item => console.log(item)))
.then(() => console.log("invoked earlier"));
Run Code Online (Sandbox Code Playgroud)

我等待“更早调用”出现在打印数字的中间。但不管项目的数量有多大。“较早调用”总是出现在最后。有人可以解释我缺少什么吗?也许这篇文章已经过时了,从那以后发生了一些变化?

Nor*_*ard 5

为了保证这一点,您实际上必须从先前的承诺中返回承诺。

无论你从一个 promise 中返回什么,都会被传递到下一个 promise。

在你的情况下,你回来了undefined。

相反,如果你返回了一个 promise,那么这个 promise 就会被解析,第二个 promise 会在这之后运行,并且会传递 promise 解析的任何值。

所以是的,promise 保证会一个接一个地运行,但是如果你选择在它们的回调中做一些异步的事情,并且不要通过返回它来将它链接回 promise,那么它们就不会费心等待为任何事情(因为他们不知道有什么可等待的)。

我假设db.remove返回一个承诺......

...所以,知道我们必须从回调中返回一个承诺,以便它真正等待异步事件的发生,我会做这样的事情。

.then(result => Promise.all(result.rows.map(row => db.remove(row.doc))))
.then((listOfRemoves) => console.log(`removed`));
Run Code Online (Sandbox Code Playgroud)

第二个函数是否接受 1 个参数或 0 个参数对于第二个函数何时运行是 100% 无关紧要的。

编辑

例子:

.then(() => setTimeout(() => console.log('2nd', 20000)))
.then(() => console.log('first'));
Run Code Online (Sandbox Code Playgroud)

发生这种情况是因为第一个然后不知道发生了 setTimeout。它不是读心者,它只是运行代码并传递返回值(在本例中为undefined)。

但是,如果返回值恰好是一个 Promise,它会等到该 Promise 完成,从中获取值,然后将其传递。

.then(() => new Promise(resolve => setTimeout(resolve, 20000)))
.then(() => console.log('I waited 20 seconds.'));
Run Code Online (Sandbox Code Playgroud)

因为它返回一个promise,它会调用then那个promise 的 ,并等待获取值,然后传递。

你的测试失败的原因是因为你基本上是这样做的。

.then(() => console.log('first'))
.then(() => console.log('second'));
Run Code Online (Sandbox Code Playgroud)

这些保证按此顺序触发。时期。所有其他人也都按这个顺序开火。只是他们也在调度异步进程,就像所有其他在 JS 中使用异步 I/O 的回调/超时一样。