Does Javascript have anything similar to VBA's DoEvents? - javascript

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

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.

You can use the setTimeout:
setTimeout(function() { }, 3600);
3600 it's the time in milliseconds:
http://www.w3schools.com/jsref/met_win_settimeout.asp

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

Related

Call a function asynchronous in a synchronous code-base

I have a project consisting out of thousands of lines of Javascript code. It contains a lot of complex calculations which are divided into different functions. One function calls three or four other functions, of which one calls another one of those four functions,... Just to say, it has a complex structure.
Some functions also need to do calculations on the DOM. E.g, One functions adds an element to the DOM, and another one needs to do a calculation using the coordinates of that element in the DOM.
However, when the user visits the page, all of this is executed by one function called - in this example - bigFunction.
The problem is that bigFunction starts a long series of complex calculations and function calls resulting in stalling the page loading, and making the page irresponsive until bigFunction is finished.
I already tried simple solutions like defering my .js-file, using $(document).ready(function() { bigFunction(); }), $(window).on('load', function() { bigFunction(); }). None of that changed anything. I tried converting my code to async code but that was crazy work.
I am actually just wondering if there is a way to call bigFunction asynchronous somehow but without converting all of my code to async code and making all my functions async, is that possible?
Some test-code:
// Executing big function
bigFunction();
// "HTML"
console.log("[+] HTML Loaded!!"); // -> Must load async with bigFunction running
// This is a function I execute on a page and which stops the page from
// loading until it's finished. How do I async this?
async function bigFunction() {
console.log("[+] bigFunction started");
if (functionToReturnTrueOrFalseAfterALongCalculation()) {
console.log("WOW!");
}
console.log("[+] bigFunction finished")
}
// Ignore logic in this function... It just returns true of false randomly after 3,5 secs
function functionToReturnTrueOrFalseAfterALongCalculation() {
console.log("[+] functionToReturnTrueOrFalseAfterALongCalculation started");
let to_return = false;
// Give back random value
let random_number = Math.floor(Math.random() * 100) + 1;
if (random_number < 50) {
to_return = true;
}
sleep(3500);
// Just to show that functions are nested deep in each other
doRandomStuff();
console.log("[+] functionToReturnTrueOrFalseAfterALongCalculation ended");
return to_return;
}
// Just another slow function
function doRandomStuff() {
console.log("[+] doRandomStuff started");
sleep(1000);
console.log("[+] doRandomStuff ended");
}
// Ignore
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
HTML should load faster...
I am actually just wondering if there is a way to call bigFunction asynchronous somehow but without converting all of my code to async code and making all my functions async, is that possible?
Fundamentally, not really. In order for the page to remain responsive, your JavaScript code must yield control back to the browser regularly. If you have some very expensive JS that blocks for 0.5 seconds, the page will appear to the user to be unresponsive for those 0.5 seconds. (0.5 seconds of blocking is not good. 0.05 seconds of blocking every 0.1 seconds could be better, for example.)
To have the site remain responsive, all of your functions that invoke sleep (the expensive calculations) need to be refactored to stagger up their jobs. Pure JavaScript jobs can be offloaded to a service worker, thereby freeing up resources to keep the active tab responsive, but jobs that require DOM manipulation (some of which you have) can't be offloaded to a service worker.
Without seeing what exactly the expensive functions are doing, more details are difficult, but for what you want, in the end you'll probably need to end up with code that looks something like
async function bigFunction() {
console.log("[+] bigFunction started");
// the below function does not block for long periods of time,
// but only for short periods of time, staggered up
const result = await functionToReturnTrueOrFalseAfterALongCalculation();
if (result) {
console.log("WOW!");
}
console.log("[+] bigFunction finished")
}
The code that comes after the execution of bigFunction would absolutely have to be able to handle the asynchronous execution flow - if you had
doSomething();
bigFunction();
finishDoingStuff();
you would have to refactor to something like
doSomething();
await bigFunction();
finishDoingStuff();
or to
doSomething();
bigFunction(finishDoingStuff);
where bigFunction calls finishDoingStuff once its expensive tasks are complete.

How to change instanly a DOM element?

I know that this might be a stupid question but it drives me crazy. I'm trying to change the innerHTML of a DOM element but it doesn't change until the end of the function's execution. For example:
function test(){
let testEl = document.getElementById('testEl')
for (let i = 0; i < 5; i++)
{
testEl.innerHTML = 'Count: ' + i;
alert(i);
}
}
Even if I have put an alert in the loop, the text of the element will not change until the end of the function's execution. How can the change be applied instantly (for example I mean during the loop)?
You can update the number every period of time using setInterval:
function test(){
let testEl = document.getElementById('testEl');
let i = 0;
const interval = setInterval(function(){
testEl.innerHTML = `Count: ${i++}`;
if(i === 5)
clearInterval(interval);
}, 1000);
}
test();
<p id="testEl"></p>
JavaScript runs in a single-threaded environment. This means that only one execution context can ever be running at any single point in time. Asynchronous code executes outside of the JavaScript runtime environment (in this case by the browser's native processing) and only when the JavaScript thread is idle can the results of an asynchronous request be executed (i.e. callbacks).
Below is an example that updates a DOM element approximately every second, creating a clock. However, if you click the button, it will ask the browser to render an alert, which is handled outside of the JavaScript runtime and is a blocking UI element, so the clock will stop. Once you clear the alert, you will see the time jump to be roughly current.
As you'll see, the asynchronous API call to window.setInterval() allows for the function to run repeatedly, every so often, and therefore not continuously. This replaces the need for a loop that runs in its entirety every time its accessed. Because of this, you can see updates to the webpage instead of the last value of your loop.
See the comments for more details:
const clock = document.querySelector("span");
// setInterval is not JavaScript. It's a call to a browser
// API asking the JS runtime to run the supplied function every
// 900 milliseconds, but that's just a request. After 900
// milliseconds, the browser will place the function on the
// JavaScript event queue and only when the JavaScript thread
// is idle will anything on the queue be executed. This is why
// the 900 milliseconds is not a guarantee - - it's just the
// minimum amount of time you'll have to wait for the function
// to run, but it could be longer if what's already running
// on the JavaScript thread takes longer than 900 milliseconds
// to complete.
window.setInterval(function(){
// Update the DOM
clock.textContent = new Date().toLocaleTimeString();
}, 900);
document.querySelector("button").addEventListener("click", function(){
// An alert is also not JavaScript, but another browser API that is executed
// by the browser, not JavaScript. However, it is a blocking (modal) UI element.
// The rest of the browser interface (including the web page) cannot update
// while the alert is present. As soon as the alert is cleared, the UI will update.
window.alert("I'm a UI blocking construct rendered by the browser, not JavaScript");
});
<div>Current time is: <span></span></div>
<button>Click for alert</button>
Another way to achieve it is by using async - Promise like this
async function test() {
let testEl = document.getElementById('testEl')
for (let i = 0; i < 5; ++i) {
testEl.innerHTML = 'Count: ' + i;
await new Promise((resolve, _) => {
setTimeout(function() {
resolve("");
}, 1000 /* your preferred delay here */);
});
}
}

Does setTimeout() run on a separate thread?

Curious to see whether setTimeout() will be fired up asynchronously, I tried the following test script:
function timedText() {
var x = document.getElementById("txt");
setTimeout(function() {
x.value = "1 second"
}, 1000);
setTimeout(function() {
x.value = "2 seconds"
}, 2000);
setTimeout(function() {
x.value = "3 seconds"
}, 3000);
while (true) {}
}
<p>Click on the button below. The input field will tell you when two, four, and six seconds have passed.</p>
<button onclick="timedText()">Display timed text</button>
<input type="text" id="txt">
Sure enough, clicking the button causes the browser to hang.
This tells me that setTimeout() does not run on a separate thread.
But on a recent interview, the interviewer suggested otherwise... Does that mean that setTimeout() is browser/implementation dependent?
JavaScript is not multi threaded. Well there are WebWorkers that run in a different thread, but that's not really multi threading, more like multiple processes, that communicate with each other.
As of that the while (true) {} will block the js context, because it is an endless loop.
The setTimeout will register a function for later execution. But at no time code will run in parallel for the same context.
A while (true) itself does not necessarily create a blocking loop:
async function sleep(time) {
return new Promise((resolve, _) => setTimeout(resolve, time))
}
async function test(val) {
while (true) {
console.log('in while loop ' + val)
await sleep(1000)
}
}
test('foo')
test('bar')
So you can say with await/async you can create some kind of cooperative multitasking like setup, but still no multi threading
There is no thread in javascript.
setTimeout push just the delegate function insto a stack that will pop for the next pass.
You can read that JavaScript and Threads
This tells me that setTimeout() does not run on a separate thread.
Yes. There is only one thread in JS.
But on a recent interview, the interviewer suggested otherwise... Does
that mean that setTimeout() is browser/implementation dependent?
As far as i know only engine changed from browser to browser. Internal mechanism stands the same - event-loop processor.
When you call setTimeout() typically control is passing back into the host environment (the browser or native node.js code for example). What is happening then is that your callback is being registered in a list of timers to execute in the future. setTimeout() will the return back to your code which will continue executing.
When your script finally completes, control again will return to the host environment which has an event loop, this loop keeps spinning until it's finally time to call your registered callback.
You can actually approximate something like this in JavaScript itself by implementing an event loop just for fun:
class EventLoop {
constructor() {
this.entries = []; // a list of all registered callbacks
this.turns = 0; // keep track of how many turns of the loop we make
}
// Adds a new callback to the list
schedule(callback, condition) {
this.entries.push([condition, callback]);
}
// To removes a callback when it's been called
remove(entry) {
this.entries.splice(this.entries.indexOf(entry), 1);
}
// Run the loop until all registered callbacks were called
// Returns the number of turns it made in the while loop
run(timeout) {
this.turns = 0;
while (this.entries.length) {
for (const entry of this.entries) {
const [condition, callback] = entry;
if (condition()) {
callback();
this.remove(entry);
}
}
this.turns++;
}
return this.turns;
}
}
We can use this EventLoop to implement something like a setTimeout():
// Define a handy log function
const log = ((startTime) => (text) => {
console.log(`t+${(Date.now() - startTime).toFixed(3)}ms: ${text}`);
})(Date.now());
// Create an event loop
const loop = new EventLoop();
// Define a setTimeout using the event loop
const defer = (fn, timeout) => {
const start = Date.now();
const end = start + timeout;
loop.schedule(fn, () => Date.now() >= end);
};
// Schedule some nested events
defer(() => {
log('I run second');
defer(() => {
log('I run third');
defer(() => {
log('I run fourth');
}, 200);
}, 200);
}, 200);
// Log syncronously
log('I run first');
// Start the event loop turning (blocks until all events are complete)
const turns = loop.run();
log(`Loop exited after ${turns} turns`);
// This will log after event loop has finished running
log('I run last');
If you run this with node.js you'll get the following output:
t+0.000ms: I run first
t+200.000ms: I run second
t+400.000ms: I run third
t+600.000ms: I run fourth
t+600.000ms: Loop exited after 6441157 turns
t+600.000ms: I run last
We just created an asynchronous timeout in pure JavaScript with a single thread. Now in reality you wouldn't do this in JavaScript, the event loop would be implemented in native code and hosted in the host environment. An example of such an event loop is libuv used by Node.js. Libuv can do things more efficiently than our toy JS example, it can put the current thread to sleep (technically it doesn't do this, it polls for IO but same concept) so it's not wasting CPU cycles.
Those asynchronous functions are handled by the browser. not the JavaScript engine. there are no threads in JavaScript.

setTimeout blocks eventloop

I was reading an article that stated that to create a nonblocking/asynchronous function, usage of setTimeout is crucial. And I thought that the function that is passed to setTimeout runs in the background. Then I read in another article that setTimeout does block the event loop when function is fired. So, I tried the following function to test.
function getit(cb) {
var i = 0;
setTimeout(function() {
while (i < 200000) { i++; console.log(i); }
cb(i);
} , 1000);
console.log(i);
}
getit(function(message) { console.log(message); });
Apparently, when 1000ms passes and it is time to execute the function, the thread is blocked and my browser freezes. My question is, if asynchronous code is not supposed to block the thread, how is that possible when the function is being executed when time elapses and not in the background? It seems like this is just a delay, but eventually, the function is going to be executed line by line and block the loop anyway. Is there something I'm missing or confused about?
Node.js execution model can be represented by the following code:
function main() {
while(running) {
var timerCb = getNextTimedoutCallback();
if( timerCb ) timerCb(); // execute it
var ioCb = getNextCompleteIOOperationCallback();
if( ioCb ) ioCb(); // execute it
}
}
Everything runs in single thread. IO operations run in worker threads and populate internal queue of completed IO operations. That getNextCompleteIOOperationCallback(); just pulls completed operation from the queue.
In the same way setTimeout() simply pushes function alongside with its end time to the timers queue (ordered by end time). And getNextTimedoutCallback() pulls next expired timeout (if any).
As you see either timer function or IO callback can block the whole thing.
That's known as cooperative multitasking.
You first have to understand that JavaScript, under most contexts is single-threaded. Be it asynchronous or not, once your script start executing, it blocks everything.
Your example above therefore enters the busy while-loop after 1000ms and begin blocking other execution.
To overcome this, the busy block has to be broken down to "yield" (Java terminology) execution cycles for other tasks.
E.g.:
function getit(cb) {
setTimeout(function() {
lessBlockingGetIt(cb);
} , 1000);
}
// Note that this is only less blocking, and much slower
function lessBlockingGetIt(cb) {
// Instead of directly looping, we batch-loop with a setTimeout
var numberOfTimes = 1000; // Reduced the size so it doesn't block like forever
var batchSize = 10;
function executeBatch(start) {
// We use a zero-timeout function is achieve a "yield" behavior so other tasks in the event loop can execute
setTimeout(function(){
var i = start, j;
for(j = 0; i < numberOfTimes && j < batchSize; i++, j++){
console.log(i);
}
if(i < numberOfTimes){
executeBatch(i);
} else {
cb(i);
console.log(i);
}
}, 0);
}
// Start the recursion loop
executeBatch(0);
}
getit(function(message) { console.log(message); });
However, you would notice the execution is much slower, and the browser is apparently less responsive than otherwise true multithreading.
To achieve real multithreading, you would need to use web workers http://www.w3schools.com/html/html5_webworkers.asp

node.js: while loop callback not working as expected

Knowing that while Node.js is working asynchronously, writing something like this:
function sleep() {
var stop = new Date().getTime();
while(new Date().getTime < stop + 15000) {
;
}
}
sleep();
console.log("done");
...would call the sleep(), block the server for the duration of the while loop (15secs) and just THEN print "done" to the console. As far as I understand, this is because Node.js is giving JavaScript only access to the main thread, and therefore this kidn of thing would halt further execution.
So I understand the solution to this is to use callbacks:
function sleep(callback) {
var stop = new Date().getTime();
while(new Date().getTime() < stop + 15000) {
;
}
callback();
}
sleep(function() {
console.log("done sleeping");
});
console.log("DONE");
So I thought this would print 'DONE' and after 15 secs. 'done sleeping', since the sleep() function gets called and is handed a pointer to a callback function. While this function is working (the while loop), the last line would be executed (print 'done'). After 15 seconds, when the sleep() function finishes, it calls the given callback function, which then prints 'done sleeping'.
Apparently I understood something wrong here, because both of the above ways block. Can anybody clarify please?
Thanks in advance,
Slagjoeyoco
Javascript and node.js are single threaded, which means a simple while blocks; no requests/events can be processed until the while block is done. Callbacks don't magically solve this problem, they just help pass custom code to a function. Instead, iterate using process.nextTick, which will give you esentially the same results but leaves space for requests and events to be processed as well, ie, it doesn't block:
function doSleep(callback) {
var stop = new Date().getTime();
process.nextTick(function() {
if(new Date().getTime() < stop + 15000) {
//Done, run callback
if(typeof callback == "function") {
callback();
}
} else {
//Not done, keep looping
process.nextTick(arguments.callee);
}
});
}
doSleep(function() {
console.log("done sleeping");
console.log("DONE");
});
You are calling sleep right away, and the new sleep function blocks. It keeps iterating until the condition is met. You should use setTimeout() to avoid blocking:
setTimeout(function () {
console.log('done sleeping');
}, 15000);
Callbacks aren't the same thing as asynchronicity, they're just helpful when you want to get a... callback... from an asynchronous operation. In your case, the method still executes synchronously; Node doesn't just magically detect that there's a callback and long-running operation, and make it return ahead of time.
The real solution is to use setTimeout instead of a busy loop on another thread.
As already mentioned, asynchronous execution should be achieved by setTimeout() rather than while, because while will freeze in one "execution frame".
Also it seems you have syntax error in your example.
This one works fine: http://jsfiddle.net/6TP76/

Categories