【问题标题】:Stopping synchronous function after 2 seconds2秒后停止同步功能
【发布时间】:2018-01-28 17:00:25
【问题描述】:

我正在使用 npm 库 jsdiff,它具有确定两个字符串之间差异的函数。这是一个同步函数,但给定两个大而不同的字符串,计算需要很长时间。

diff = jsdiff.diffWords(article[revision_comparison.field], content[revision_comparison.comparison]);

此函数在通过 Express 处理请求的堆栈中调用。为了用户,我怎样才能让体验更容易忍受?我认为我的两个选择是:

  1. 以某种方式取消同步功能。
  2. 以某种方式取消用户请求。 (但这会让函数继续运行吗?)

编辑:我应该注意,给定两个非常大且不同的字符串,我希望在代码中发生不同的逻辑。因此,简单地等待进程完成是不必要的,而且在负载上很麻烦——我绝对不希望它运行很长时间。

【问题讨论】:

    标签: javascript node.js express


    【解决方案1】:

    fork a child process 对于该特定任务,您甚至可以创建一个队列来限制在给定时刻可以运行的子进程的数量。

    这里有一个工人的基本示例,它将原始快递reqres 发送给执行重度同步的子进程。操作不会阻塞主(主)线程,一旦完成,就会返回主线程。

    Worker(分叉示例):

    process.on('message', function(req,res) {
      /* > Your jsdiff logic goes here */
      //change this for your heavy synchronous :
      var input = req.params.input;
      var outcome = false;
      if(input=='testlongerstring'){outcome = true;}
      // Pass results back to parent process :
      process.send(req,res,outcome);
    });
    

    还有来自你的大师

    var cp = require('child_process');
    var child = cp.fork(__dirname+'/worker.js');
    
    child.on('message', function(req,res,outcome) {
      // Receive results from child process
      console.log('received: ' + outcome);
      res.send(outcome); // end response with data
    });
    

    您可以完美地将一些工作与reqres 一起发送给孩子,就像这样(来自大师):(想象app = express)

    app.get('/stringCheck/:input',function(req,res){
     child.send(req,res);
    });
    

    【讨论】:

      【解决方案2】:

      我在jsdiff's repository找到了这个:

      所有接受可选回调方法的方法都将在省略该参数时以同步模式运行,并在提供时以异步模式运行。这允许更大的差异而不阻塞事件循环。这可以直接作为最终参数传递,也可以作为选项对象中的回调字段传递。

      这意味着您应该能够添加回调作为最后一个参数,从而使函数异步。它看起来像这样:

        jsdiff.diffWords(article[x], content[y], function(err, diff) {
          //add whatever you need
        });
      

      现在,您有多种选择:

      1. 直接返回给用户,保持函数在后台运行。

      2. 使用 setTimeout 设置 2 秒超时(或任何适合您的应用程序的限制),如本文所述 answer.

      如果您选择选项 2,您的代码应如下所示

        jsdiff.diffWords(article[x], content[y], function(err, diff) {
          //add whatever you need
          return callback(err, diff);
        });
        //if this was called, it means that the above operation took more than 2000ms (2 seconds)
        setTimeout(function() { return callback(); }, 2000);
      

      【讨论】: