我已经对此提出了一些问题,但是也许这个问题会带来更好的答案(我很不擅长问题)
我有一个名为FOO的类,在这里我调用了异步启动函数,该函数启动了创建FOO类的过程。该FOO类执行许多不同的计算,并使用node.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"));
被称为,请求“ statuscode”的响应在请求完成后仍会打印出来。
我得到的answares显示了一些我不知道的好的逻辑,但是问题是它们都只停止了请求,处理请求响应的代码仍然会执行,并且在某些情况下会触发新的请求。这意味着我必须向Stop功能发送垃圾邮件,直到完全停止它为止。
答案 0 :(得分:1)
将问题分解为一堆进行串行化异步操作的函数调用,并且希望用户能够单击“取消/停止”按钮并导致异步操作链中止(例如,停止执行任何操作并保释)获得最终尝试得到的结果)。
我可以想到几种方案。
1。每个操作都会检查一些state
属性。您可以使这些操作全部成为某个具有aborted
状态属性的对象的一部分。每个异步操作的代码在完成后都必须检查该state属性。可以连接“取消/停止”按钮来设置此状态变量。当前异步操作完成后,它将中止其余操作。如果您使用诺言来对您的操作进行排序(看起来确实如此),那么您可以拒绝当前的诺言,从而导致整个链中止。
2。创建一些自动为您合并了取消state
的异步包装函数。如果您所有实际的异步操作都是一小部分操作(例如全部使用request
模块),则您可以围绕使用的任何请求操作创建包装函数,当任何操作完成时,它都会为您检查状态变量或将其合并到返回的promise中,如果状态变量已停止,则拒绝返回的promise会导致整个promise链中止。这样做的好处是,您只需要在一个位置进行if
检查,其余代码就可以切换为使用包装的请求功能版本,而不是常规功能。
3。将所有异步步骤/逻辑放入可以杀死的另一个进程中。(在我看来)就像使用大锤解决一个小问题,但是您可以启动child_process(也可以是node.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()
,如果按下停止按钮,它将自动拒绝。然后,您需要对异步逻辑进行编码,这样,如果有任何拒绝,它将中止(通常是promise anywa的默认和自动行为)。
答案 1 :(得分:0)
异步函数不会在单独的线程中运行代码,它们只是将异步控制流封装在语法糖中,并返回一个表示其完成状态(即,待处理/已解决/已拒绝)的对象。
之所以要如此区分,是因为一旦通过调用异步函数启动了控制流,它就必须一直持续到完成或直到第一个未捕获的错误为止。
如果您想取消它,则必须声明一个状态标志,并在所有或某些顺序点上对其进行检查,即在await
表达式之前和return
之前(或{{ 1}})(如果已设置标志)。有三种方法可以做到这一点。
throw
功能,该功能可以设置状态。cancel()
函数,该函数将返回状态,或者根据状态有条件地抛出。isCancelled()
的函数,该函数将在请求取消时抛出,然后在每个序列点上,将Promise
更改为await yourAsyncFunction();
下面是最后一种方法的示例。
await Promise.race([cancellationPromise, yourAsyncFunction()]);