Reputation: 2052
I bumped into a funny problem, I was trying to invoke a function (which has a setTimeout in its body) from an infite loop, and it was never getting called, but when the loop is changed into not infite the call was happening!!
this works :-
var bar = function() {
setTimeout(function() {
console.log("1");
}, 0);
};
var count = 4;
while(count > 0){
bar();
count --;
}
this never works . : -
var bar = function() {
setTimeout(function() {
console.log("1");
}, 1000);
};
while (true) {
bar();
}
can anybody explain whats going on in here! how to call a function with a setTimeout in its body from an infinite loop?
Upvotes: 2
Views: 189
Reputation: 736
how to call a function with a setTimeout in its body from an infinite loop?
Like that:
let done = false;
let count = 0;
const bar = () => {
setTimeout(() => {
console.log(`bar ${count}`);
count++;
}, 5); // delay#1
}
const eventLoopQueue = () => {
return new Promise(resolve =>
setImmediate(() => {
console.log('event loop');
setTimeout(() => {
if (count > 10) {
done = true;
}
resolve();
}, 5) //delay#2 should be equal or greater than delay#1
})
);
}
const run = async () => {
while (!done) {
bar();
await eventLoopQueue();
}
}
run().then(() => console.log('Done'));
Output:
event loop
bar 0
event loop
bar 1
event loop
bar 2
event loop
bar 3
event loop
bar 4
event loop
bar 5
event loop
bar 6
event loop
bar 7
event loop
bar 8
event loop
bar 9
event loop
bar 10
Done
This approach is good when you need to do something in long or infinite loop with conditional exit and have an access to the events (mouse clicks, any socket.io events and so on).
let done = false;
const eventEmitter = setInterval(() => {
console.log('Hello! Are you here? Hello!');
}, 0)
// This setTimeout just ends infinite loop in some time
// For demonstation purpose only
setTimeout(() => {
done = true;
clearInterval(eventEmitter);
}, 20);
// This gives us access to event loop queue
// If the events wait in the queue they will be released
const eventLoopQueue = () => {
return new Promise(resolve =>
setImmediate(() => {
console.log('Next please! How can I help you?');
resolve();
})
);
}
// run while loop
const run = async () => {
while (!done) {
console.log('I am busy! Doing some work: part #1');
await eventLoopQueue();
console.log('I am busy! Doing some work: part #1');
await eventLoopQueue();
console.log('I am busy! Doing some work: part #1');
await eventLoopQueue();
}
}
run().then(() => console.log('Done'));
Upvotes: 0
Reputation: 8239
This is the way the event loops in javascript works.
The setTimeout() callbacks are being added to the macro task queue. The macrotask queue is processed only when no other JavaScript is mid-execution,or when the call stack is empty. So, the the setTimeout()
callbacks can't execute until the while loop (and code it contains) finishes and returns control back to the browser.
So in example1:
var bar = function() {
setTimeout(function() {
console.log("1");
}, 0);
};
var count = 4;
while(count > 0){
bar();
count --;
}
The while execution completes and returns control back to the browser(callstack is empty), so all the setTimeout()
callbacks are executed one by one.
while in example2:
var bar = function() {
setTimeout(function() {
console.log("1");
}, 1000);
};
while (true) {
bar();
}
it is an infinite loop, so control is never returned to the browser or the call stack is never empty, so the event loop cannot process the macrotask queue, so the setTimeout()
callbacks are never executed.
Upvotes: 1
Reputation: 721
It's because your JavaScript code is single-threaded. The infinite while loop keeps the browser busy and no time to execute other async functions including your bar
function.
Upvotes: 1
Reputation: 102
Let's see first how this (infinite) code executes
CURRENT EXECUTION WAITING FOR EXECUTION (IN QUEUE)
=================== ================================
=> variable bar is assigned to a function
=> while (true)
=> call bar()
=> inside bar()
=> setTimeout function will be sent to queue => Execute setTimeout’s function after 1000ms(1)
=> while (true)
=> call bar()
=> inside bar()
=> setTimeout function will be sent to queue => Execute setTimeout’s function after 1000ms(2)
=> call bar()
=> inside bar()
=> setTimeout function will be sent to queue => Execute setTimeout’s function after 1000ms(3)
.
.
. while (true) => happens forever => waits forever…
If you want to call a function with a setTimeout inside an infinite loop, then you can use something like this,
var bar = function() {
setTimeout(function() {
console.log("1");
runBar();
}, 1000);
};
function runBar() {
bar();
}
runBar();
Upvotes: 1
Reputation: 92440
This is because of the way the event loop works. Async events like timeouts are queued up and are processed after the main body of your script runs-to-completion. This means it is waiting for your while
loop to finish before it evens starts looking at the setTimeout
callbacks. This is one (of several) reasons why you don't want to block the thread with long-running synchronous code like giant loops in javascript. Nothing else can happen while your infinite while
loop is spinning.
Upvotes: 7