Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Javascript have anything similar to VBA's DoEvents?

I have a long running for-loop in my code and I'd like to delay to loop to handle other tasks in the event queue (like a button press). Does javascript or JQuery have anything that could help me? Basically I'm trying to do something similar to delaying loops like here (https://support.microsoft.com/en-us/kb/118468).

like image 348
Ph33ly Avatar asked Jul 15 '15 19:07

Ph33ly


2 Answers

If your application really requires long-running JavaScript code, one of the best ways to deal with it is by using JavaScript web workers. JavaScript code normally runs on the foreground thread, but by creating a web worker you can effectively keep a long-running process on a background thread, and your UI thread will be free to respond to user input.

As an example, you create a new worker like this:

var myWorker = new Worker("worker.js");

You can then post messages to it from the js in the main page like this:

myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');

And respond to the messages in worker.js like this:

onmessage = function(e) {
  console.log('Message received from main script');
  var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
  console.log('Posting message back to main script');
  postMessage(workerResult);
}
like image 65
Maximillian Laumeister Avatar answered Oct 05 '22 19:10

Maximillian Laumeister


With the introduction of generators in ES6, you can write a helper method that uses yield to emulate DoEvents without much syntactic overhead:

doEventsOnYield(function*() {
    ... synchronous stuff ...

    yield;  // Pump the event loop. DoEvents() equivalent.

    ... synchronous stuff ...
});

Here's the helper method, which also exposes the completion/failure of the function as a Promise:

function doEventsOnYield(generator) {
    return new Promise((resolve, reject) => {
        let g = generator();
        let advance = () => {
            try {
                let r = g.next();
                if (r.done) resolve();
            } catch (ex) {
                reject(ex);
            }
            setTimeout(advance, 0);
        };
        advance();
    });
}

Note that, at this time, you probably need to run this through an ES6-to-ES5 transpiler for it to run on common browsers.

like image 22
Craig Gidney Avatar answered Oct 05 '22 17:10

Craig Gidney