Running JS in a killable 'thread'; detecting and canceling long-running processes

北城余情 提交于 2019-12-08 02:09:35

问题


Summary: How can I execute a JavaScript function, but then "execute" (kill) it if it does not finish with a timeframe (e.g. 2 seconds)?

Details

I'm writing a web application for interactively writing and testing PEG grammars. Unfortunately, the JavaScript library I'm using for parsing using a PEG has a 'bug' where certain poorly-written or unfinished grammars cause infinite execution (not even detected by some browsers). You can be happily typing along, working on your grammar, when suddenly the browser locks up and you lose all your hard work.

Right now my code is (very simplified):

grammarTextarea.onchange = generateParserAndThenParseInputAndThenUpdateThePage;

I'd like to change it to something like:

grammarTextarea.onchange = function(){
  var result = runTimeLimited( generateParserAndThenParseInput, 2000 );
  if (result) updateThePage();
};

I've considered using an iframe or other tab/window to execute the content, but even this messy solution is not guaranteed to work in the latest versions of major browsers. However, I'm happy to accept a solution that works only in latest versions of Safari, Chrome, and Firefox.


回答1:


Web workers provide this capability—as long as the long-running function does not require access to the window or document or closures—albeit in a somewhat-cumbersome manner. Here's the solution I ended up with:

main.js

var worker, activeMsgs, userTypingTimeout, deathRowTimer;
killWorker(); // Also creates the first one

grammarTextarea.onchange = grammarTextarea.oninput = function(){
  // Wait until the user has not typed for 500ms before parsing
  clearTimeout(userTypingTimeout);
  userTypingTimeout = setTimeout(askWorkerToParse,500);
}

function askWorkerToParse(){
  worker.postMessage({action:'parseInput',input:grammarTextarea.value});
  activeMsgs++;                                // Another message is in flight
  clearTimeout(deathRowTimer);                 // Restart the timer
  deathRowTimer = setTimeout(killWorker,2000); // It must finish quickly
};

function killWorker(){
  if (worker) worker.terminate();   // This kills the thread
  worker = new Worker('worker.js')  // Create a new worker thread
  activeMsgs = 0;                   // No messages are pending on this new one
  worker.addEventListener('message',handleWorkerResponse,false);
}

function handleWorkerResponse(evt){
  // If this is the last message, it responded in time: it gets to live.
  if (--activeMsgs==0) clearTimeout(deathRowTimer);
  // **Process the evt.data.results from the worker**
},false);

worker.js

importScripts('utils.js') // Each worker is a blank slate; must load libs

self.addEventListener('message',function(evt){
  var data = evt.data;
  switch(data.action){
    case 'parseInput':
      // Actually do the work (which sometimes goes bad and locks up)
      var parseResults = parse(data.input);

      // Send the results back to the main thread.
      self.postMessage({kind:'parse-results',results:parseResults});
    break;
  }
},false);


来源:https://stackoverflow.com/questions/21956169/running-js-in-a-killable-thread-detecting-and-canceling-long-running-processe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!