基本上我有两个异步函数。其中一个是简单的 5 秒超时,另一个是具有多个步骤的复杂异步函数。 这是一个例子
const delay = ms => new Promise(res => setTimeout(res, ms));
class Runner {
async start() {
let printStuff = async () => {
for(let i = 0 ; i < 50; i++){
console.log(i);
await delay(50);
}
}
let printLetters = new Promise(async function(resolve, reject) {
const length = Math.floor(Math.random() * 10)
//dont know how long this will take
for(let i = 0; i < length; i++){
await printStuff();
}
resolve('letters');
});
let timeout = new Promise(async function(resolve, reject) {
await delay(5000);
resolve('timeout')
});
const finished = await Promise.all([timeout, printLetters]);
if(finished === 'timeout'){
this.stop();
}
}
stop(){
//stop printing stuff instantly
}
}
const myRunner = new Runner();
myRunner.start();
//could call myRunner.stop(); if the user canceled it
我实现它的方式将添加一个全局变量并在 for 循环中包含 if 语句以检查是否已调用中断,但我想知道是否有更好的方法来实现这一点。此解决方案的一个问题是它会打印更多数字。我将不得不向另一个 for 循环添加另一个检查,这可能会很快变得混乱。
这是一个使用我自己的库的简单演示。
import { CPromise } from "c-promise2";
const task = CPromise.promisify(function* () {
let printStuff = CPromise.promisify(function* () {
for (let i = 0; i < 10; i++) {
console.log(i);
yield CPromise.delay(100);
}
});
const length = Math.floor(Math.random() * 10) + 3;
//dont know how long this will take
for (let i = 0; i < length; i++) {
yield printStuff();
}
return "letters";
});
const promise = task()
.timeout(5000)
.then(
(result) => console.log(`Done: ${result}`),
(err) => console.warn(`Fail: ${err}`)
);
setTimeout(() => {
promise.cancel();
}, 2000);
这就是你所追求的吗?
更改内容:
Promise.all
替换为Promise.race- 添加了
isStopped
prop,使"具有多个步骤的复杂异步函数">跳过其余步骤的执行。 不过,它不会杀死它。承诺不可取消。
const delay = ms => new Promise(res => setTimeout(res, ms));
class Runner {
isStopped = false;
async start() {
const printStuff = async () => {
let i = 0;
while (!this.isStopped) {
console.log(i++);
await delay(50);
}
}
const printLetters = new Promise(
resolve => printStuff()
.then(() => resolve('letters'))
);
const timeout = new Promise(
resolve => delay(5000)
.then(() => resolve('timeout'))
);
const finished = await Promise.race([timeout, printLetters]);
console.log({ finished });
if (finished === 'timeout') {
this.stop();
}
}
stop() {
this.isStopped = true;
}
}
const myRunner = new Runner();
myRunner.start();
<button onclick="myRunner.stop()">stop</button>
初始答案 (保留它,因为评论引用它,而不是上面的内容;以防有人在 2074 年发现它有用):
这是一个概述我在评论中建议的示例。 下面的run()
返回在1s
后发生的拒绝者和在0
和2s
之间随机解决的履行填充器之间的竞赛。
const rejector = (timeout) => new Promise((resolve, reject) => {
setTimeout(reject, timeout, 'rejected')
});
class Runner {
run() {
return Promise.race([
rejector(1000),
new Promise((resolve, reject) => {
setTimeout(resolve, Math.random() * 2000, 'fulfilled')
})
])
}
}
const t0 = performance.now();
[...Array(6).fill()].forEach((_, key) => {
const runner = new Runner();
runner.run()
.then(r => console.log(`Proomise ${key} ${r} after ${performance.now() - t0}ms`))
.catch(err => console.log(`Promise ${key} ${err} after ${performance.now() - t0}ms`));
})
注意:最初我将拒绝器放在类中,但(至少对于上面的示例)我不明白为什么它不应该留在外面(在真实情况下,从帮助程序文件导入)。
如果需要瞬时停止功能,则可能需要将打印作业作为外部脚本执行。然后使用这样的子进程。
const { spawn } = require('child_process');
class Runner {
......
start() {
this.job[somejobId] = spawn('command to execute script');
//this can be anything, including a node script, e.g. `node start.js`
.....
}
stop(jobId) {
if (jobId) {
//this will kill the script that you spawn above
this.job[jobId].kill('SIGHUP');
}
}
stopAllJobs() {
// looping through the job queue to kill all the jobs
this.job.forEach(job => job.kill('SIGHUP'))
}
}
您将从节点文档网站获得有关如何启动子进程的更多信息 https://nodejs.org/api/child_process.html#subprocesskillsignal
如果您的作业(外部脚本)停滞不前,建议您仅在至少有 2 个 CPU 内核时才使用上述代码,否则如果您的脚本很重,它会影响您的主进程。