简体   繁体   中英

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

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

You can use the setTimeout:

setTimeout(function() { }, 3600);

3600 it's the time in milliseconds:

http://www.w3schools.com/jsref/met_win_settimeout.asp

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.

There is no exact equivalent to DoEvents . Something close is using setTimeout for each iteration:

(function next(i) {
    // exit condition
    if (i >= 10) {
        return;
    }

    // body of the for loop goes here

    // queue up the next iteration
    setTimeout(function () {
        // increment
        next(i + 1);
    }, 0);
})(0); // initial value of i

However , that's rarely a good solution, and is almost never necessary in web applications. There might be an event you could use that you're missing. What's your real problem?

Here's a tested example of how to use Yield as a direct replacement for DoEvents.

(I've used Web Worker and it's great, but it's far removed from DoEvents and near-impossible to access global variables). This has been formatted for ease of understanding and attempts to show how the extras required (to make the function handle yield) could be treated as an insertion within the original function. "yield" has all sorts of other features, but used thus, it is a near direct replacement for DoEvents.

 //'Replace DoEvents with Yield ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield ) var misc = 0; //'sample external var function myfunction() { //'This is the beginning of your original function which is effectively replaced by a handler inserted as follows.. //'-----------------------------------Insert Handler.. var obj = myfuncGen.next(); //'start it if (obj.done == false) { setTimeout(myfunction, 150); //'adjust for the amount of time you wish to yield (depends how much screen drawing is required or etc) } } var myfuncGen = myfuncSurrogate(); //'creates a "Generator" out of next. function* myfuncSurrogate() { //'This the original function repackaged! Note asterisk. //'-------------------------------------End Insert var ms; //...your original function continues here.... for (var i = 1; i <= 9; i++) { //'sample 9x loop ms = new Date().getTime(); while (new Date().getTime() < ms + 500); //'PAUSE (get time & wait 500ms) as an example of being busy misc++; //'example manipulating an external var outputdiv.innerHTML = "Output Div<br>demonstrating progress.. " + misc; yield; //'replacement for your doevents, all internal stack state and variables effectively hibernate. } console.log("done"); } myfunction(); //'and start by calling here. Note that you can use "return" to return a value except by call backs. 
 <div id='outputdiv' align='center'></div> 

..If you are new to all this, be aware that without the insertion and the yield keyword, you would simply wait 5 seconds while nothing happened and then the progress {div} would read "9" (because all the other changes to {div} were invisible).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM