souparno majumder
souparno majumder

Reputation: 2052

why does the setTimeout not get called when invoked from inside an infinite while loop

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

Answers (5)

AlbertS
AlbertS

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

amrender singh
amrender singh

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

AnthonyY
AnthonyY

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

Kapil Dev S
Kapil Dev S

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

Mark
Mark

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

Related Questions