2

I'm using the npm library jsdiff, which has a function that determines the difference between two strings. This is a synchronous function, but given two large, very different strings, it will take extremely long periods of time to compute.

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

This function is called in a stack that handles an request through Express. How can I, for the sake of the user, make the experience more bearable? I think my two options are:

  1. Cancelling the synchronous function somehow.
  2. Cancelling the user request somehow. (But would this keep the function still running?)

Edit: I should note that given two very large and different strings, I want a different logic to take place in the code. Therefore, simply waiting for the process to finish is unnecessary and cumbersome on the load - I definitely don't want it to run for any long period of time.

Cœur
  • 37,241
  • 25
  • 195
  • 267
db2791
  • 1,040
  • 6
  • 17
  • 30

2 Answers2

2

fork a child process for that specific task, you can even create a queu to limit the number of child process that can be running in a given moment.

Here you have a basic example of a worker that sends the original express req and res to a child that performs heavy sync. operations without blocking the main (master) thread, and once it has finished returns back to the master the outcome.

Worker (Fork Example) :

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);
});

And from your Master :

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
});

You can perfectly send some work to the child along with the req and res like this (from the Master): (imagine app = express)

app.get('/stringCheck/:input',function(req,res){
 child.send(req,res);
});
EMX
  • 6,066
  • 1
  • 25
  • 32
1

I found this on jsdiff's repository:

All methods above which accept the optional callback method will run in sync mode when that parameter is omitted and in async mode when supplied. This allows for larger diffs without blocking the event loop. This may be passed either directly as the final parameter or as the callback field in the options object.

This means that you should be able to add a callback as the last parameter, making the function asynchronous. It will look something like this:

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

Now, you have several choices:

  1. Return directly to the user and keep the function running in the background.

  2. Set a 2 second timeout (or whatever limit fits your application) using setTimeout as outlined in this answer.

If you go with option 2, your code should look something like this

  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);