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

时间:2019-07-12 22:23:25

标签: node.js electron

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

我有一个名为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功能发送垃圾邮件,直到完全停止它为止。

2 个答案:

答案 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()]);