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
Related
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 */);
});
}
}
I have a chess program that searches moves using a recursive alphaBeta algorithm (and some layers on top of that). I want to stop searching after 10 seconds (but I try with 1 second while making it work), so I use setTimeout to set a global flag.
let timeOut = false;
let i = 0;
setTimeout(() => {
timeOut = true;
console.log('timeout');
console.log('timeout');
console.log('timeout');
console.log('timeout');
}, 1000);
while (i < 1000 && timeOut === false) {
score = mtdf(pos, i, score)
console.log('depth:' + i + ', size: ' + tp.size());
i++;
}
This sure runs in the browser so I know the console.log statements should be printed in the browser but they aren't. The callback isn't called. Help please.
Edit: Added timeOut variable initialisation to make it more clear. Also, sorry, forgot to add i initialization (not that these declarations are even relevant to the question, since the problem is that setTimeout callback isn't even being executed...)
setTimeout will run the function you pass to it:
After the specified time has passed
After the minimum time for a timeout has passed
When the event loop isn't busy doing something else
… whichever is the latest.
You can't use a timeout to interrupt your while loop. The JavaScript engine is too busy running the while loop to check to see if there are any functions waiting to run on the timeout queue.
Your function will be called after the loop has finished when the JS engine is no longer busy.
Don't try to interrupt your loop from the outside.
Before you enter the while loop, record the current time.
Each time you go around the loop, compare the current time with the recorded time.
If the difference is greater than the period you want to allow the loop to run from, exit the loop
Javascript is single threaded meaning that execution of "asynchronous" statements like setTimeout, setInterval, and XMLHttpRequest get queued on a single thread. If something blocks the thread (like your heavy while() loop) async statements get delayed.
See here for more elaborate answer
To make your code work, I suggest looking at the timestamp
var t0 = new Date().getTime(), t1 = t0;
while(true)
{
// do stuff here
t1 = new Date().getTime();
if (t1 % 100 === 0) console.log(t1); // [debug ]print something every 100ms
if (t1 - t0 > 1000) break; // kill the loop after one second
}
setTimeout is asynchronous meaning the program won't wait for the setTimeout to finish and the code will continue executing.
You need to use Promise to wait for the setTimout to finish so your code will behave properly.
Also, the problem of your code also lies in the variable declarations, you didn't declare the i and timeOut variables.
let timeOut = false;
new Promise((resolve) => {
setTimeout(() => {
timeOut = true;
console.log('timeout');
console.log('timeout');
console.log('timeout');
console.log('timeout');
resolve();
}, 1000);
}).then(() => {
let i = 0;
while (i < 1000 && timeOut === false) {
score = mtdf(pos, i, score)
console.log('depth:' + i + ', size: ' + tp.size());
i++;
}
});
If multiple callbacks are registered at the same time, is it guaranteed that the first callback to be called will be the one with the least delay?
Consider the following code:
function foo(i) {
setTimeout(function () {
console.log(i);
}, i);
}
for (var i = 0; i < 1000; i++) {
foo(i);
}
Is this code guaranteed to output 1-1000 in order?
Please note: This question is not about using equal delays. It has been established that multiple setTimeout() calls with equal delays are not guaranteed to execute in order.
Yes!!
function foo(i) {
setTimeout(function () {
console.log(i);
}, i);
}
for (var i = 1; i <= 1000; i++) {
foo(i);
}
Explanation :
As you are Calling setTimeout it will execute independently doesn't matter how much setTimeout are running or will run,So its dependent on time you call and timing you set.Hope you get this.
I know many of the people have already answered this question. But, most of the people don't know exactly how the event loop works. Even I got it recently. So, I thought it would be helpful to all whoever gets to this post.
setTimeout() function works in accordance with the event callback and render queue. Rendering queue is the reason why it is not guaranteed that your callback would execute after the exact timeout. There are concepts like
call stack,
web apis and
callback queue
You have this below function called multiple times,
function foo(i) {
setTimeout(function () {
console.log(i);
}, i);
}
so the flow would be something like, each call to foo(i) would first go to call stack. Once the call stack reaches to its top (when loop is over), one by one the web api(browser specific)would handle the poped out function from the stack and then it moves to the callback queue when the timeout has reached. Depending on the availability that is, if rendering is not happening, the callback gets executed.
Inside the callback queue the functions are lined up in the order of calling.
So, the order is guaranteed.
A better place to understand the event queue is the below article.
Philip Roberts: What the heck is the event loop?
I'm trying to get my around callbacks in nodeJS. I thought I had them down until I came across the following example:
function async(callback) {
for(var i = 0; i < 10; i++) {
}
callback();
}
async(function() {
console.log('Expected 2nd Printout');
});
console.log('Expected 1st Printout');
My understanding of node and callbacks would be that after the async function has been invoked, "Expected 1st Printout" would be printed while we're waiting for the async function to finish executing then "Expected 2nd Printout" would be printed when it has finished. However, upon executing this simple program, the opposite occurs.
I'm thinking that because this is a processing task and not a situation where the async function is waiting for a file or listening for some network response that the program is run in a procedural way instead of an asynchronous way?
You can use process.nextTick() to make your function asynchronous. Official documentation:
https://nodejs.org/docs/latest/api/process.html#process_process_nexttick_callback
Once the current event loop turn runs to completion, call the callback function. This is not a simple alias to setTimeout(fn, 0), it's much more efficient. It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop.
Also try to google for "Understanding the Node.js Event Loop" there are plenty of articles which will help you to understand what happens behind the scene.
Run this code:
function async(callback) {
process.nextTick( function() {
for(var i = 0; i < 10; i++) {
console.log('i = ' + i);
}
callback();
});
}
async(function() {
console.log('Expected 2nd Printout');
});
console.log('Expected 1st Printout');
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/