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
任务很简单:确保每个promise只在另一个promise之后运行(. so()
)。
由于某种原因,我找不到一个方法来做这件事。
我尝试了生成器函数(yield
),尝试了返回promise的简单函数,但最终都归结为同一个问题:循环是同步的。
对于async,我只需使用async.series()
。
你怎么解决?
基于trincot的出色回答,我写了一个可重用的函数,它接受一个处理程序来运行数组中的每个项。函数本身返回一个promise,允许您等待循环完成,您传递的处理程序函数也可以返回一个promise。
我花了一些时间才把它弄对,但我相信下面的代码在很多promise循环的情况下都是可用的。
复制粘贴就绪代码:
// SEE https://stackoverflow.com/a/46295049/286685
const loop = (arr, fn, busy, err, i=0) => {
const body = (ok,er) => {
try {const r = fn(arr[i], i, arr); r && r.then ? r.then(ok).catch(er) : ok(r)}
catch(e) {er(e)}
}
const next = (ok,er) => () => loop(arr, fn, ok, er, ++i)
const run = (ok,er) => i < arr.length ? new Promise(body).then(next(ok,er)).catch(er) : ok()
return busy ? run(busy,err) : new Promise(run)
}
要使用它,请将数组作为第一个参数进行调用,将处理函数作为第二个参数进行调用。不要为第三、第四和第五个参数传递参数,它们是在内部使用的。
const loop = (arr, fn, busy, err, i=0) => {
const body = (ok,er) => {
try {const r = fn(arr[i], i, arr); r && r.then ? r.then(ok).catch(er) : ok(r)}
catch(e) {er(e)}
}
const next = (ok,er) => () => loop(arr, fn, ok, er, ++i)
const run = (ok,er) => i < arr.length ? new Promise(body).then(next(ok,er)).catch(er) : ok()
return busy ? run(busy,err) : new Promise(run)
}
const items = ['one', 'two', 'three']
loop(items, item => {
console.info(item)
})
.then(() => console.info('Done!'))
您可以为此使用async/await
。我会解释得更多,但实际上没什么。这只是一个常规的for
循环,但在构建您的promise之前,我添加了wait
关键字
我喜欢的是,您的promise可以解析正常值,而不是像您的代码(或此处的其他答案)所包含的那样产生副作用。这给了你塞尔达传说中的力量:一个链接到过去,你可以影响光明世界和黑暗世界中的事物——也就是说,你可以在promise的数据可用之前/之后轻松处理数据,而不必求助于深度嵌套的函数、其他笨拙的控制结构或愚蠢的IIFE。
// where DarkWorld is in the scary, unknown future
// where LightWorld is the world we saved from Ganondorf
LightWorld ... await DarkWorld
这就是它的样子。。。
async function someProcedure (n) {
for (let i = 0; i < n; i++) {
const t = Math.random() * 1000
const x = await new Promise(r => setTimeout(r, t, i))
console.log (i, x)
}
return 'done'
}
someProcedure(10)
.then(console.log)
.catch(console.error)
正如您在问题中已经暗示的,您的代码同步创建所有promise。相反,它们只能在前一个解析时创建。
其次,使用新promise
创建的每个promise都需要通过调用解析
(或拒绝
)来解析。这应该在计时器过期时完成。这将触发任何回调,然后您将对该promise进行回调。而这样的
然后
回调(或等待
)是实现该链的必要条件。
有了这些成分,有几种方法可以执行这种异步链接:
>
使用
Array#reduce
以立即解析promise开始
通过一个函数传递自己作为分辨率回调
使用ECMAScript 2017的
异步
/等待
语法
使用ECMAScript 2020的
进行等待…的
语法
但是让我先介绍一个非常有用的通用函数。
使用
setTimeout
是可以的,但是我们实际上需要一个在计时器过期时解决的promise。让我们创建这样一个函数:这称为promisify函数,在本例中,我们将promisifysetTimeout
。它将提高代码的可读性,并可用于上述所有选项:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
请参阅下面每个选项的代码段和注释。
您可以使用
for
循环,但必须确保它不会同步创建所有promise。取而代之的是,您创建一个初始的立即解决promise,然后将新promise与前一个解决promise联系起来:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
for (let i = 0, p = Promise.resolve(); i < 10; i++) {
p = p.then(() => delay(Math.random() * 1000))
.then(() => console.log(i));
}
问题内容: 我正在使用Java Swing编写游戏。我想在每次循环执行时绘制一下,并在之间稍加延迟以在屏幕上创建级联效果。我相信系统中的效率例程会将调用折叠为一个调用。无论如何,所有更改都在总延迟后立即发生。是否有某种方法可以强制系统立即重新绘制,然后在循环的每次迭代中延迟? 我的代码: 问题答案: 您可以用来强制立即重绘。 编辑:再次阅读您的问题后,对我来说,您可能正在事件分发线程上执行逻辑。这
我正在制作一个基于文本的冒险游戏,我需要进行一些用户输入验证 我已经为这个特定实例设置了循环,但在输入正确的输入后,我似乎无法使循环中断。 代码: 我的输出是这样的: 你的选择是敲打还是猛击。别那么粗鲁。 大满贯 你猛地撞上沉重的橡木门,沉重的铁铰链砰的一声让位了。当你凝视城堡的第一个房间时,灰尘落在你的头上。大厅大约有五十英尺长。西墙有三扇门,东墙有两扇门。空气中弥漫着霉菌、湿气和铁的味道。曾经
这里的函数是,但是如果外部循环条件是
我找不到我的谜题。当我加载页面时,chrome会说:localhost页面不工作,localhost重定向了你太多次。我猜这是在创造一个循环。我可能做错了一些我无法得到的事情。这是我的密码。 授权控制器 模型 Authenticate.php config/auth.php
我目前有两个循环,一个获取时间戳,另一个while循环根据时间戳查找映射信息并以某种方式输出。 我的问题是我目前正在循环浏览一个文本,并希望它在第二个循环的isdo="N"时再次从头开始读取文件,但是,情况似乎并非如此。 迄今为止的代码:
这是我的代码: 我的问题是如何检查分数是否在范围内,如果输入了错误的输入,如何使循环重复。当我试图运行程序并输入一个超过范围的输入时,它会转到下一个评估数字,而不是再次尝试,直到它是真的。