如何阻止类/函数继续在 Node.js 中执行代码



我已经对此提出了一些问题,但也许这个问题会产生更好的答案(我不擅长提问)

我有一个名为 FOO 的类,我在其中调用异步启动函数,该函数启动了 FOO 类要执行的过程。这个FOO类做了很多不同的计算,以及使用节点.js"requets"模块发布/获取计算。

-我正在使用电子UI(通过按下按钮,执行功能等)来创建和启动FOO类-

class FOO {
async Start(){
console.log("Start")
await this.GetCalculations();
await this.PostResults()
}

async PostResults(){
//REQUESTS STUFF
const response = {statusCode: 200} //Request including this.Cal
console.log(response)
//Send with IPC

//ipc.send("status", response.statusCode)
}

async GetCalculations(){
for(var i = 0; i < 10; i++){
await this.GetCalculation()
}
console.log(this.Cal)
}
async GetCalculation(){
//REQUEST STUFF
const response = {body: "This is a calculation"} //Since request module cant be used in here.
if(!this.Cal) this.Cal = [];
this.Cal.push(response)
}
}

var F1 = new FOO();

F1.Start();

现在想象一下这段代码,但有更多的步骤和更多的请求等,完成类 FOO 中的所有任务可能需要几秒钟/几分钟。

-Electron有一个停止按钮,用户可以在希望计算停止时点击该按钮-

我将如何阻止整个班级继续?

在某些情况下,用户可能会停止并立即启动,因此我一直在尝试找出一种方法来完全阻止代码运行,但是用户仍然可以创建一个新类并启动它,而无需在后台运行另一个类。

我一直在考虑"微小工人"模块,但在创建工人时,它需要 1-2 秒,这降低了快速计算程序的目的。

希望这个问题比其他问题更好。

更新:

应用不同答案背后的逻辑,我想出了这个:

await Promise.race([this.cancelDeferred, new Promise( async (res, req) => {
var options ={
uri: "http://httpstat.us/200?sleep=5000"
}
const response = await request(options);
console.log(response.statusCode)
})])

但即使当

this.cancelDeferred.reject(new Error("User Stop"));

被调用,请求"状态码"的响应在请求完成时仍会打印出来。

我得到的 answares 显示了一些我不知道的好逻辑,但问题是它们都只停止请求,请求响应的代码仍然会执行,并且在某些情况下会触发新请求。这意味着我必须向 Stop 函数发送垃圾邮件,直到它完全停止它。

将问题框定为一大堆进行序列化异步操作的函数调用,并且您希望用户能够点击"取消/停止"按钮并导致异步操作链中止(例如,停止再做任何事情并放弃获得它试图获得的任何最终结果)。

我能想到的几种方案。

1.每个操作都会检查一些state属性。使这些操作全部成为具有aborted状态属性的某个对象的一部分。每个异步操作的代码必须在完成后检查该状态属性。 可以挂接"取消/停止"按钮来设置此状态变量。 当前异步操作完成后,它将中止操作的其余部分。 如果您正在使用 promise 对操作进行排序(看起来您确实如此),那么您可以拒绝当前的承诺,从而导致整个链中止。

2.创建一些异步包装器函数,自动为您合并取消state如果您所有实际的异步操作都是一小组操作(例如所有操作都使用request模块),那么您可以围绕您使用的任何请求操作创建一个包装函数,当任何操作完成时,它会为您检查状态变量或将其合并到返回的承诺中,如果它已被停止, 它拒绝返回的承诺,这会导致整个承诺链中止。 这样做的好处是,您只需在一个地方执行if检查,其余代码只需切换到使用请求函数的包装版本而不是常规版本。

3. 将所有异步步骤/逻辑放入另一个可以杀死的进程中。这似乎(对我来说)就像使用大锤来解决一个小问题,但您可以启动一个child_process(也可以是节点.js程序)来执行多步骤异步操作,当用户按下停止/取消时,您只需杀死子进程。 监视child_process并等待结果的代码将获得最终结果或指示它已停止。 您可能希望在此处使用实际进程而不是工作线程,以便完全中止,从而正确回收该进程使用的所有内存和其他资源。

请注意,这些解决方案都没有使用任何类型的无限循环或轮询循环。


例如,假设实际的异步操作使用的是request()模块。

您可以定义一个高范围的承诺,如果用户单击取消/停止按钮,该承诺将被拒绝:

function Deferred() {
let p = this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
this.then = this.promise.then.bind(p);
this.catch = this.promise.catch.bind(p);
this.finally = this.promise.finally.bind(p);
}    
// higher scoped variable that persists
let cancelDeferred = new Deferred();
// function that gets called when stop button is hit
function stop() {
// reject the current deferred which will cause 
//    existing operations to cancel
cancelDeferred.reject(new Error("User Stop"));
// put a new deferred in place for future operations
cancelDeferred = new Deferred();
}
const rp = require('request-promise');
// wrapper around request-promise
function rpWrap(options) {
return Promise.race([cancelDeferred, rp(options)]);
}

然后,您只需在任何地方调用rpWrap()而不是调用rp(),如果点击停止按钮,它将自动拒绝。 然后,您需要对异步逻辑进行编码,以便在任何拒绝时,它将中止(这通常是承诺 anywa 的默认和自动行为)。

异步函数不会在单独的线程中运行代码,它们只是用语法糖封装异步控制流,并返回一个表示其完成状态(即挂起/已解决/已拒绝)的对象。

进行这种区分的原因是,一旦通过调用异步函数启动控制流,它必须继续,直到完成,或者直到第一个未捕获的错误。

如果你想能够取消它,你必须声明一个状态标志,并在所有或某些序列点检查它,即在await表达式之前,如果设置了标志,则提前(或throw)return。有三种方法可以做到这一点。

  • 您可以向调用方提供一个cancel()函数,该函数将能够设置状态。
  • 你可以接受来自调用方的isCancelled()函数,该函数将返回状态,或者根据状态有条件地抛出。
  • 您可以接受一个返回Promise的函数,该将在请求取消时抛出,然后在每个序列点上,将await yourAsyncFunction();更改为await Promise.race([cancellationPromise, yourAsyncFunction()]);

下面是最后一种方法的示例。

async function delay (ms, cancellationPromise) {
return Promise.race([
cancellationPromise,
new Promise(resolve => {
setTimeout(resolve, ms);
})
]);
}
function cancellation () {
const token = {};
token.promise = new Promise((_, reject) => {
token.cancel = () => reject(new Error('cancelled'));
});
return token;
}
const myCancellation = cancellation(); 
delay(500, myCancellation.promise).then(() => {
console.log('finished');
}).catch(error => {
console.log(error.message);
});
setTimeout(myCancellation.cancel, Math.random() * 1000);

最新更新