通过已解决的 Promise 跳出 for(ever) 循环
Break out of for(ever) loop by resolved Promise
我希望这是一个简单的问题,但我目前无法理解。
我想要做的是跳出一个 while 循环,该循环包含承诺得到解决时的延迟。
在伪代码中,这看起来像:
while( ! promise.resolved ){
doSomthing()
await sleep( 5min )
}
循环 必须在 promise 解决后立即中断 而不是等待 sleep
完成。
sleep
目前由 setTimeout
简单地实现,但可以以不同的方式实现。
我希望在 awaited promise 和 sleep 之间有某种空间分离,以更清楚地显示它的工作*)(因为我希望有一个优雅的解决方案可供学习)。
那么但我不喜欢的方法是:
while( true ){
doSomething()
try {
await Promise.race([promise,rejectAfter(5000)])
break
} catch( e ){}
}
如果你必须知道:
doSomething
正在发送状态信息。
promise
正在等待用户交互。
*) 此代码的部分目的是 show/demonstrate 其他人希望事情如何运作。所以我正在寻找这个实现级别上最清晰的解决方案。
对您的想法进行细微修改以使其更好地发挥作用:
const sleep = ms =>
new Promise(resolve => setTimeout(resolve, ms));
async function waitAndDo(promise) {
let resolved = false;
const waitFor = promise
.then((result) => resolved = true);
while(!resolved) {
doSomething();
await Promise.race([waitFor, sleep(5000)]);
}
}
- 该函数接受承诺并将一直工作直到它解决。
waitFor
承诺将在 promise
完成并且 resolved
更新为 true
后完成。
- 然后
while
循环可以循环直到 resolved
变量设置为 true
。在这种情况下,循环将结束并在它之后继续执行。
- 在循环内,
Promise.race()
将确保它会在 promise 解析 或 睡眠到期后立即停止等待。以先到者为准。
因此,一旦承诺得到解决,.then()
处理程序就会首先触发并更新 resolved
。 await Promise.race();
将结束等待,并且 while
循环不会再次执行,因为 resolved
现在是 true
.
似乎你会使用某种间隔并在 promise 完成时杀死它。
const updateMessage = (fnc, ms, runInit) => {
if (runInit) fnc();
const timer = window.setInterval(fnc, ms);
return function () {
console.log('killed');
timer && window.clearTimeout(timer);
}
}
const updateTime = () => {
document.getElementById("out").textContent = Date.now();
}
const updateEnd = updateMessage(updateTime, 100, true);
new Promise((resolve) => {
window.setTimeout(resolve, Math.floor(Math.random()*5000));
}).then(updateEnd);
<div id="out"></div>
作为 VLAZ 的替代品非常合理 , you can avoid the separate boolean sentinel by having your sleep function return some kind of unique sentinel return value that indicates the the timeout. Symbol 正是这种用例的轻量级、独特的对象。
function sleepPromise(ms, resolveWith) {
return new Promise(resolve => {
setTimeout(resolve, ms, resolveWith);
});
}
const inputPromise = new Promise(
resolve => document.getElementById("wakeUp").addEventListener("click", resolve));
async function yourFunction() {
const keepSleeping = Symbol("keep sleeping");
do {
/* loop starts here */
console.log("Sleeping...");
/* loop ends here */
} while (await Promise.race([inputPromise, sleepPromise(3000, keepSleeping)]) === keepSleeping);
console.log("Awake!");
}
yourFunction();
<button id="wakeUp">Wake up</button>
一种方法是等待承诺结果,假设它是一个真值1:
const promise = someTask();
let result = undefined;
while (!result) {
doSomething();
result = await Promise.race([
promise, // resolves to an object
sleep(5000), // resolves to undefined
]);
}
1:如果不是,则将 .then(_ => true)
链接到 promise
,或者使 sleep
满足您可以区分的特殊值来自 someTask
可能 return 的一切(就像杰夫回答中的符号)。
也可以很好地与 do
-while
循环一起使用,因为 result
一开始总是未定义的:
const promise = someTask();
let result = undefined;
do {
doSomething();
result = await Promise.race([
promise, // resolves to an object
sleep(5000), // resolves to undefined
]);
} while (!result);
这里的一个缺点是,如果 doSomething()
抛出异常,则永远不会等待承诺,并且可能会在任务出错时导致未处理的拒绝崩溃。
另一种方法是不使用循环,而是使用 old-school 间隔:
const i = setInterval(doSomething, 5000);
let result;
try {
result = await someTask();
} finally {
clearInterval(i);
}
这里的缺点是 doSomething
不会立即调用,而是在 5 秒后才第一次调用。此外,如果 doSomething
抛出异常,它会立即使应用程序崩溃。如果您不希望 doSomething
抛出(或处理 setInterval
回调中的每个异常并期望“循环”继续进行),它仍然可能是一个好方法。
转发来自 someTask()
和 doSomething()
的所有异常的“正确”方法可能如下所示:
let done = false;
const result = await Promise.race([
(async() => {
while (!done) {
doSomething();
await sleep(5000)
}
})(),
someTask().finally(() => {
done = true;
}),
]);
(除了 .finally()
,您还可以将 Promise.race
包装在 try
-finally
中,就像方法二一样。)
与方法二相比,唯一的小缺点是 sleep(5000)
将保留 运行,并且不会在 someTask
完成时立即取消(即使 result
立即可用),这可能会阻止您的程序立即退出。
我希望这是一个简单的问题,但我目前无法理解。
我想要做的是跳出一个 while 循环,该循环包含承诺得到解决时的延迟。
在伪代码中,这看起来像:
while( ! promise.resolved ){
doSomthing()
await sleep( 5min )
}
循环 必须在 promise 解决后立即中断 而不是等待 sleep
完成。
sleep
目前由 setTimeout
简单地实现,但可以以不同的方式实现。
我希望在 awaited promise 和 sleep 之间有某种空间分离,以更清楚地显示它的工作*)(因为我希望有一个优雅的解决方案可供学习)。 那么但我不喜欢的方法是:
while( true ){
doSomething()
try {
await Promise.race([promise,rejectAfter(5000)])
break
} catch( e ){}
}
如果你必须知道:
doSomething
正在发送状态信息。
promise
正在等待用户交互。
*) 此代码的部分目的是 show/demonstrate 其他人希望事情如何运作。所以我正在寻找这个实现级别上最清晰的解决方案。
对您的想法进行细微修改以使其更好地发挥作用:
const sleep = ms =>
new Promise(resolve => setTimeout(resolve, ms));
async function waitAndDo(promise) {
let resolved = false;
const waitFor = promise
.then((result) => resolved = true);
while(!resolved) {
doSomething();
await Promise.race([waitFor, sleep(5000)]);
}
}
- 该函数接受承诺并将一直工作直到它解决。
waitFor
承诺将在promise
完成并且resolved
更新为true
后完成。- 然后
while
循环可以循环直到resolved
变量设置为true
。在这种情况下,循环将结束并在它之后继续执行。 - 在循环内,
Promise.race()
将确保它会在 promise 解析 或 睡眠到期后立即停止等待。以先到者为准。
因此,一旦承诺得到解决,.then()
处理程序就会首先触发并更新 resolved
。 await Promise.race();
将结束等待,并且 while
循环不会再次执行,因为 resolved
现在是 true
.
似乎你会使用某种间隔并在 promise 完成时杀死它。
const updateMessage = (fnc, ms, runInit) => {
if (runInit) fnc();
const timer = window.setInterval(fnc, ms);
return function () {
console.log('killed');
timer && window.clearTimeout(timer);
}
}
const updateTime = () => {
document.getElementById("out").textContent = Date.now();
}
const updateEnd = updateMessage(updateTime, 100, true);
new Promise((resolve) => {
window.setTimeout(resolve, Math.floor(Math.random()*5000));
}).then(updateEnd);
<div id="out"></div>
作为 VLAZ 的替代品非常合理
function sleepPromise(ms, resolveWith) {
return new Promise(resolve => {
setTimeout(resolve, ms, resolveWith);
});
}
const inputPromise = new Promise(
resolve => document.getElementById("wakeUp").addEventListener("click", resolve));
async function yourFunction() {
const keepSleeping = Symbol("keep sleeping");
do {
/* loop starts here */
console.log("Sleeping...");
/* loop ends here */
} while (await Promise.race([inputPromise, sleepPromise(3000, keepSleeping)]) === keepSleeping);
console.log("Awake!");
}
yourFunction();
<button id="wakeUp">Wake up</button>
一种方法是等待承诺结果,假设它是一个真值1:
const promise = someTask();
let result = undefined;
while (!result) {
doSomething();
result = await Promise.race([
promise, // resolves to an object
sleep(5000), // resolves to undefined
]);
}
1:如果不是,则将 .then(_ => true)
链接到 promise
,或者使 sleep
满足您可以区分的特殊值来自 someTask
可能 return 的一切(就像杰夫回答中的符号)。
也可以很好地与 do
-while
循环一起使用,因为 result
一开始总是未定义的:
const promise = someTask();
let result = undefined;
do {
doSomething();
result = await Promise.race([
promise, // resolves to an object
sleep(5000), // resolves to undefined
]);
} while (!result);
这里的一个缺点是,如果 doSomething()
抛出异常,则永远不会等待承诺,并且可能会在任务出错时导致未处理的拒绝崩溃。
另一种方法是不使用循环,而是使用 old-school 间隔:
const i = setInterval(doSomething, 5000);
let result;
try {
result = await someTask();
} finally {
clearInterval(i);
}
这里的缺点是 doSomething
不会立即调用,而是在 5 秒后才第一次调用。此外,如果 doSomething
抛出异常,它会立即使应用程序崩溃。如果您不希望 doSomething
抛出(或处理 setInterval
回调中的每个异常并期望“循环”继续进行),它仍然可能是一个好方法。
转发来自 someTask()
和 doSomething()
的所有异常的“正确”方法可能如下所示:
let done = false;
const result = await Promise.race([
(async() => {
while (!done) {
doSomething();
await sleep(5000)
}
})(),
someTask().finally(() => {
done = true;
}),
]);
(除了 .finally()
,您还可以将 Promise.race
包装在 try
-finally
中,就像方法二一样。)
与方法二相比,唯一的小缺点是 sleep(5000)
将保留 运行,并且不会在 someTask
完成时立即取消(即使 result
立即可用),这可能会阻止您的程序立即退出。