在javascript中处理函数中断的最佳方法是什么?



基本上我有两个异步函数。其中一个是简单的 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
  • 添加了isStoppedprop,使"具有多个步骤的复杂异步函数">跳过其余步骤的执行。 不过,它不会杀死它。承诺不可取消。

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后发生的拒绝者和在02s之间随机解决的履行填充器之间的竞赛。

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 内核时才使用上述代码,否则如果您的脚本很重,它会影响您的主进程。

相关内容

最新更新