for (let i = 0; i < 10; i++) {
const promise = new Promise((resolve, reject) => {
const timeout = Math.random() * 1000;
setTimeout(() => {
console.log(i);
}, timeout);
});
// TODO: Chain this promise to the previous one (maybe without having it running?)
}
上面将给出以下随机输出:
6
9
4
8
5
1
7
2
3
0
任务很简单:确保每个诺言仅在另一个诺言(.then()
)之后运行。
由于某种原因,我找不到办法。
我尝试了生成器函数(yield
),尝试了返回promise的简单函数,但总的来说,它总是归结为同一个问题: 循环是同步的 。
使用异步,我只使用async.series()
。
您如何解决?
正如您已经在问题中暗示的那样,您的代码同步创建了所有promise。相反,仅应在上一个解析时创建它们。
其次,new Promise
需要使用调用resolve
(或reject
)来解决创建的每个promise。计时器到期时应执行此操作。这将触发then
您对该承诺的任何回调。为了实现链,这种then
回调(或await
)是必需的。
有了这些成分,有几种方法可以执行此异步链接:
以for
立即解决承诺开始的循环
从此Array#reduce
开始,立即解决承诺
具有将自身作为分辨率回调传递的函数
使用ECMAScript2017的async
/ await
语法
使用建议的ECMAScript2020的for await...of
语法
请在下面查看每个选项的摘要和注释。
for
您 可以 使用for
循环,但是必须确保它不会newPromise
同步执行。相反,您可以创建一个初始的立即解决的Promise,然后在先前的Promise解析时链接新的Promise:
for (let i = 0, p = Promise.resolve(); i < 10; i++) {
p = p.then(_ => new Promise(resolve =>
setTimeout(function () {
console.log(i);
resolve();
}, Math.random() * 1000)
));
}
reduce
这只是先前策略的一种更实用的方法。您创建一个长度与要执行的链相同的数组,并以立即解决的承诺开始:
[...Array(10)].reduce( (p, _, i) =>
p.then(_ => new Promise(resolve =>
setTimeout(function () {
console.log(i);
resolve();
}, Math.random() * 1000)
))
, Promise.resolve() );
当您实际上有 一个包含要在诺言中使用的数据的数组时,这可能会更有用。
(function loop(i) {
if (i < 10) new Promise((resolve, reject) => {
setTimeout( () => {
console.log(i);
resolve();
}, Math.random() * 1000);
}).then(loop.bind(null, i+1));
})(0);
这将创建一个名为的函数loop
,在代码的最后,您可以看到它立即被参数0 调用。这是计数器,还有 i
参数。如果该计数器仍低于10,该函数将创建一个新的Promise,否则链接将停止。的调用resolve()
将触发then
回调,该回调将再次调用该函数。loop.bind(null, i+1)
只是一种不同的表达方式_ => loop(i+1)
。
async
/await
现代JS引擎支持以下语法:
(async function loop() {
for (let i = 0; i < 10; i++) {
await new Promise(resolve => setTimeout(resolve, Math.random() * 1000));
console.log(i);
}
})();
看起来 好像是new Promise()
调用是同步执行的,这看起来可能很奇怪,但实际上,该async
函数在执行first时返回await
。每次等待的promise解析后,该函数的运行上下文都将还原,并在之后执行await
,直到遇到下一个,然后继续进行直到循环结束。
由于根据超时返回承诺是很平常的事情,因此您可以创建一个单独的函数来生成此类承诺。在这种情况下,这称为“ 赋 函数”
setTimeout
。它可以提高代码的可读性:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
(async function loop() {
for (let i = 0; i < 10; i++) {
await delay(Math.random() * 1000);
console.log(i);
}
})();
for await...of
甚至在最近,该for await...of
语法也被一些JavaScript引擎使用。尽管在这种情况下它并没有真正减少代码,但是它允许将随机间隔链的定义与它的实际迭代隔离开:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
async function * randomDelays(count ,max) {
for (let i = 0; i < count; i++) yield delay(Math.random() * max).then(() => i);
}
(async function loop() {
for await (let i of randomDelays(10, 1000)) console.log(i);
})();
问题内容: 我试图与本地异步循环ES6承诺这 样的 作品,但不正确。我想我在某个地方犯了一个大错误,我需要有人告诉我它在哪里以及如何正确完成 现在输出: 好的部分:它到了尽头。 不好的部分:它捕获了一些错误,我不知道为什么。 问题答案: 我见过的任何带有promise循环的辅助函数实际上使它变得比您可以使用递归开箱即用的方法差很多。 是的,但是更好一点: 在jsfiddle中查看http://js
问题内容: 用诺言做诸如while循环之类的事情的惯用方式是什么。所以: 如果条件仍然存在,请执行某项操作,然后再次重复执行其他操作。 我这样做是想知道是否有更好/更理想的方法? 输出:1 2 3 4 5 6 7 8 9 10 11完成 问题答案: 我会用一个对象包装值。这样,您可以拥有一个属性来让循环知道您已完成。
问题内容: 如何正确构造一个循环以确保以下的 promise调用 和链接的 logger.log(res) 通过迭代同步运行? 尽管它似乎有效,但是我认为它不能保证调用 logger.log(res); 的顺序 。 有什么建议? 问题答案: 我不认为这可以保证调用logger.log(res)的顺序; 实际上,确实如此。该语句在调用之前执行。 有什么建议? 很多。最重要的是您使用手动创建承诺反模式
问题内容: 关于这两个重要来源:NZakas- 承诺链中的归还承诺 和MDN承诺,我想提出以下问题: 每次我们从承诺履行处理程序返回值时,该值如何传递给从同一处理程序返回的新承诺? 例如, 在这个例子中,是一个承诺。也是来自履行处理程序的承诺。但是。取而代之的是神奇地解决(如何?),然后将该值传递给的实现处理程序。即使是这里的句子也令人困惑。 您能给我解释一下这到底是怎么回事吗?我对这个概念感到困
问题内容: 在循环中使用/ 是否有任何问题?我试图遍历文件数组和每个文件的内容。 这段代码确实有效,但是这可能会出问题吗?我让某人告诉我,您不应该在这样的高阶函数中使用/ ,所以我只是想问一下这是否有问题。 问题答案: 确保代码确实有效,但是我很确定它不会执行您期望的功能。它只会触发多个异步调用,但此后函数会立即返回。 顺序阅读 如果要顺序读取文件, 则不能使用。只需使用现代循环即可,该循环将按预
问题内容: AngularJS文档说: $ qpromise由模板引擎以角度识别,这意味着在模板中,您可以将附加到作用域的promise视为它们的结果值。 所以有人可以解释一下这种提琴无法正常工作的原因吗?不可能更改文本字段的值。但是分配保证$http服务返回作用域字段的工作就像一个超级按钮。 控制器: HTML: 问题答案: 您需要在promise对象上使用then()函数: 就您而言,我认为您