I'm wondering if these two blocks of code are the same in Node.js?
// Style 1
setTimeout(function () {
console.log('hello');
}, 0);
// Style 2
console.log('hello');
Since above I'm passing 0 for the timeout, there should be no waiting time. Is it identical to just calling console.log('hello'); directly without using setTimeout?
They are different, the first adds the function to the event queue, so that it can execute as soon as it gets a chance after the current execution path completes. The second will execute it immediately.
For example:
console.log('first');
setTimeout(function(){
console.log('third');
}, 0);
console.log('second');
The order that those are printed in is well defined, you could even do something slow (but synchronous) before printing 'second'. It's guaranteed that console.log('second'); will still execute before the callback to setTimeout does:
console.log('first');
setTimeout(function () {
console.log('third'); // Prints after 8 seconds
}, 0);
// Spinlock for 3 seconds
(function(start){ while(new Date - start < 3000); })(new Date);
console.log('second'); // Prints after 3 seconds, but still before 'third'
// Spinlock for 5 seconds
(function(start){ while(new Date - start < 5000); })(new Date);
Strictly speaking they are not exactly the same - setTimeout gives the browser a chance to "catch up" with any tasks that it desperately needs to do. But as far as we're normally concerned, 99% of the time they will do the same thing.
Related
Long story short, i'm working in the chrome developer console and i have a javascript function (that i decided to call main for illustration) that makes 4 function calls but each of the 4 function calls need to be separated by a few seconds to allow the page to update properly and the user to react to the resulting dialogue boxes.
The main function WORKS beautifully when i add a setTimeout with a delay to each of the 4 function calls inside and run it ONCE. it does NOT work when i try to loop the main function X times.
From what i can gather from reading other similar problems and documentation, this has to do with the complexities of how setTimeout works and how the javascript engine in the chrome browser is single-threaded... or something? Not my expertise but i tried to do my due diligence, ultimately i do not know how to fix the problem and even searching other Q/A's and documentation i could not arrive at a working solution
I've tried the below code with the setTimeout( , delay * i) method and with the setTimeout( , delay) with no index from the for loop being passed in to multiply the delay, both did not work correctly.
Other questions recommend using nested setTimeouts, i'm not sure how to accomplish that here? and others recommend using setInterval, but i'm not sure how to accomplish that a programmatic amount of times, as each of my surveys has a certain number of questions, and i don't want the number of repetitions of the main function to exceed the maximum amount of questions in a survey.
for(let i = 1; i < 15+1; i++) {
main(i);
}
function main(i){
setTimeout(takescreenshot, 2000*i);
setTimeout(checkforanswers,5000*i);
setTimeout(takescreenshot, 8000*i);
setTimeout(function(){$("[name=next_question_button]")[0].click();},11000*i);
}
The main function is supposed to loop an arbitrary number of times each time, and in my head, the program flow is supposed to be:
execute the "takescreenshot" function after a 2 second delay from executing main.
execute the "checkforanswers" function 5 seconds after main is called and 3 seconds after the first function is called in main. this gap between step 1-2 allows the user to respond to the print dialogue box that arises.
3.execute the "takescreenshot" function 8 seconds after main is called, and 3 seconds after the previous step, allowing the page DOM elements to be updated correctly for the new screenshot to reflect changes from the first screen shot.
execute a click on the "next_question" button to advance to the next question in my survey, 11 seconds after main is called and 3 seconds after the last screen shot is taken to advance onto the next question, allowing 4 steps to be repeated on a new question of the survey.
When executed once, it works exactly as I want, but when looped multiple times, step 1-4 begin to occur without the proper delay and start to happen before the relevant "wait for X" in each of the steps has been satisfied and it all mushes together achieving unpredictable and unwanted results.
If i is 2 then setTimeout(takescreenshot, 2000*i); will be called 2000*2 milliseconds after main was called.
But you want that it is called 11000 + 2000 milliseconds after main was called. The same is for the other timeouts.
As i starts with 1 you want to use 11000 * (i - 1) as a delay added to each timeout function.
function main(i){
let delay = 11000 * (i - 1);
setTimeout(takescreenshot, 2000 + delay );
setTimeout(checkforanswers, 5000 + delay );
setTimeout(takescreenshot, 8000 + delay );
setTimeout(function(){$("[name=next_question_button]")[0].click();}, 11000 + delay);
}
Your algorithm is off. You should be multiplying i by the time it takes for a full iteration to complete, and then adding 2, 5, or 8 seconds to it instead. For example, if the desired time from one iteration's takescreenshot to the next iteration's takescreenshot is 14000 ms, use:
function main(i){
setTimeout(takescreenshot, 2000 + (i * 14000));
setTimeout(checkforanswers,5000 + (i * 14000));
setTimeout(takescreenshot, 8000 + (i * 14000));
setTimeout(function(){$("[name=next_question_button]")[0].click();},11000 + (i * 14000));
}
The order of operations would be clearer if you promisified the function and awaited each call in the for loop:
(async () => {
for (let i = 1; i < 15 + 1; i++) {
await main();
}
})();
function main() {
setTimeout(takescreenshot, 2000);
setTimeout(checkforanswers, 5000);
setTimeout(takescreenshot, 8000);
return new Promise((resolve) => {
setTimeout(function() {
$("[name=next_question_button]")[0].click();
resolve();
}, 11000);
});
}
Here is the classic chunking approach:
function chunks([first, ...other], delay = 2000, result) {
const nextResult = first(result);
setTimeout(() => chunks(other, delay, nextResult), delay);
}
const simpleFunction = () => { return 'whatever'; };
const functionWithParams = (a, b) => a + b;
const pipe = [
simpleFunction,
() => functionWithParams(3, 5),
data => console.log('inline code to run, which uses prev result (3+5=8): ' + data),
];
chunks(pipe);
// or chunks(pipe, 5000);
Your for loop does not wait so you are just making timeout calls that are milliseconds apart for the entire loop. So you are basically calling takescreenshot 15 times.
Just use main to call itself. In the last timeout, call main to repeat.
function main(i){
setTimeout(takescreenshot, 2000*i);
setTimeout(checkforanswers,5000*i);
setTimeout(takescreenshot, 8000*i);
setTimeout(function(){
$("[name=next_question_button]")[0].click();}
if (i<14) {
main(i+1)
}
}, 11000*i)
}
main(0)
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++;
}
});
Below is the code present in "practise01.js" file,
function fn(name){
return f;
function f(){
var n = name;
console.log("Next TICK "+n+", ");
}
}
function myTimeout(time,msg){
setTimeout(function(){
console.log("TIMEOUT "+msg);
},time);
}
process.nextTick(fn("ONE"));
myTimeout(500,"AFTER-ONE");
process.nextTick(fn("TWO"));
myTimeout(500,"AFTER-TWO");
process.nextTick(fn("THREE"));
myTimeout(500,"AFTER-THREE");
process.nextTick(fn("FOUR"));
The output from running above code is
rahul#rahul:~/myPractise/PlainNodeJSPractise01/Process$ node practise01.js
Next TICK ONE,
Next TICK TWO,
Next TICK THREE,
Next TICK FOUR,
TIMEOUT AFTER-ONE
TIMEOUT AFTER-TWO
TIMEOUT AFTER-THREE
Now I wrote the code without using process.nextTick, in "practise02.js", as follows,
function myTimeout(time,msg){
setTimeout(function(){
console.log("TIMEOUT "+msg);
},time);
}
function fn(name){
return f;
function f(){
var n = name;
console.log("Next TICK "+n+", ");
}
}
fn("ONE")();
myTimeout(500,"AFTER-ONE");
fn("TWO")();
myTimeout(500,"AFTER-TWO");
fn("THREE")();
myTimeout(500,"AFTER-THREE");
fn("FOUR")();
after running the above code the output is
rahul#rahul:~/myPractise/PlainNodeJSPractise01/Process$ node practise02.js
Next TICK ONE,
Next TICK TWO,
Next TICK THREE,
Next TICK FOUR,
TIMEOUT AFTER-ONE
TIMEOUT AFTER-TWO
TIMEOUT AFTER-THREE
If you see both the outputs are same.
So in which case I need to go with process.nextTick ?
When I tried to read more, what I came to understand is If I need to execute some function immediately when the eventloop is empty than go for "process.nextTick".
So how does its different from my second approach.
Please explain me or give me some pointers
The node documentation is actually pretty good in explaining when and why using nextTick:
https://nodejs.org/api/process.html#process_process_nexttick_callback_args
What it does:
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.
and when to use:
This is important when developing APIs in order to give users the opportunity to assign event handlers after an object has been constructed but before any I/O has occurred...
function definitelyAsync(arg, cb) {
if (arg) {
process.nextTick(cb);
return;
}
fs.stat('file', cb);
}
definitelyAsync(true, () => {
foo();
});
bar(); // will now allways be called before foo()
First lets understand the behaviour of process.nextTick()
Eventloop has phases where in each phase different type of async functions get executed.
process.nextTick(callback[, ...args]) is a part of the asynchronous API of nodeJS. But it is not technically part of the event loop.
nextTickQueue will be processed after the current operation is completed, regardless of the current phase of the event loop.
any time we call process.nextTick() in a given phase of eventloop, callback passed to process.nextTick() will be resolved before the event loop continues.
Why we need to use process.nextTick
It is very important for APIs to be either 100% synchronous or 100% asynchronous. Consider this example:
// WARNING! DO NOT USE! BAD UNSAFE HAZARD!
function maybeSync(arg, cb) {
if (arg) {
cb();
return;
}
fs.stat('file', cb);
}
const maybeTrue = Math.random() > 0.5;
maybeSync(maybeTrue, () => {
foo();
});
bar();
It is not clear whether foo() or bar() will be called first.
The following approach is much better:
function definitelyAsync(arg, cb) {
if (arg) {
process.nextTick(cb);
return;
}
fs.stat('file', cb);
}
already #Johannes Merz has mentioned in his answer why it's good to use process.nextTick(callback) rather than setTimeout(callback, 0)
For more refer here
You have your answer in your post where you share your output with us in:
rahul#rahul:~/myPractise/PlainNodeJSPractise01/Process$ node practise02.js
Next TICK ONE,
Next TICK TWO,
Next TICK THREE,
Next TICK FOUR,
TIMEOUT AFTER-ONE
TIMEOUT AFTER-TWO
TIMEOUT AFTER-THRE
If we change your timeout interval from 500 to 0 still same result:
function fn(name){
return f;
function f(){
var n = name;
console.log("Next TICK "+n+", ");
}
}
function myTimeout(time,msg){
setTimeout(function(){
console.log("TIMEOUT "+msg);
},time);
}
process.nextTick(fn("ONE"));
myTimeout(0,"AFTER-ONE");// set timeout to execute in 0 seconds for all
process.nextTick(fn("TWO"));
myTimeout(0,"AFTER-TWO");
process.nextTick(fn("THREE"));
myTimeout(0,"AFTER-THREE");
process.nextTick(fn("FOUR"));
Results
Next TICK ONE,
Next TICK TWO,
Next TICK THREE,
Next TICK FOUR,
TIMEOUT AFTER-ONE
TIMEOUT AFTER-TWO
TIMEOUT AFTER-THREE
when you use process.nextTick you basically ensure that the function that you pass as a parameter will be called immediately in the next tick ie. start of next event loop.So that's why all your function in next tick executes before your timer ie. setTimeout next tick doesn't mean next second it means next loop of the nodejs eventloop. Also next tick ensures that the function you are trying to call is executed asynchronously. And next tick has higher priority than your timers, I/O operations etc queued in the eventloop for execution. You should use nextTick when you want to ensure that your code is executed in next event loop instead of after a specified time. nextTick is more efficient than timers and when you want to ensure the function you call is executed asynchronously . You can find more information on this in nodejs docs
This code crashes chrome:
var ms = 1000;
function wait(ms) {
function _done() {
done = true;
alert('timer: ' + timer + ', done: ' + done);
clearTimeout(timer);
}
var done = false;
var timer;
alert('starting timer');
while (!done) {
timer = setTimeout(_done, ms);
}
alert('wait done');
}
Why? How do I get this right?
There are a lot of fundamental problems here.
First, I'm assuming that somewhere not depicted in your code, you're actually calling the wait function by passing it ms.
It looks like you want the variables done and timer to be accessible in your function _done. To ensure that, you should init your variables before you define the function.
setTimeout is async. Javascript is single threaded unless you go out of your way to start up other threads (web workers). setTimeout will not freeze processing for ms amount of time. It adds an event that will run if nothing else is executing. Because it is not blocking, your while loop will loop again to create another timeout, and another... ad infinium. None of the many timeouts will interrupt the while loop because of its single threaded nature. It's more like the Windows Message Queue. Since _done doesn't interrupt and set done to true, it'll continually spin off more and more timeouts until an inevitable crash.
If you're wanting to delay code execution by a certain amount of time, you setTimeout once and execution will begin after that wait as long as nothing else is executing.
function _done() {
alert('done!');
}
setTimeout(_done, 1000);
Another note, I don't think you have to clearTimeout for a timeout that has already timed out. Its only used to prevent a timeout from happening before it times out. This means that nine times out of ten, you can ignore the return value of setTimeout.
When you are setting the timer variable inside the loop, the loop does not wait for the timeout to execute before the loop continues.
So you are overwriting your timeout every time the loop occurs and the code will never be executed because your while loop does not take longer than a second to run the code.
See here for a similar problem : Wait until a condition is true?
You code will essentially change to something like the following :
var ms = 1000;
var done = false;
var timer;
function _done() {
done = true;
alert('timer: ' + timer + ', done: ' + done);
clearTimeout(timer);
wait(ms);
}
function wait(ms) {
if(!done) {
alert('starting timer');
timer = setTimeout(_done, ms);
} else {
//Is Done...
}
}
Once you call the wait function it will check to see if the done variable is true, if not it will call the _done function to set it to true. The _done function will then re-call the wait function to execute the condition again.
you are missing the following line
timer = setTimeout(_done(), ms);
JSFIDDLE
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/