JavaScript - setTimeout

JavaScript - setTimeout

setTimeout

What is setTimeout ?

setTimeout() is a JavaScript Function which is utilised to induce a delay in performing a task (Or a set of instructions). setTimeout does not block subsequent instructions until the statements within it are executed. Subsequent instructions gets executed in the order in which they appear on call stack. It's an Asynchronous Operation (Non-blocking in nature).

Non-Blocking Nature

As the Call Stack encounters a setTimeout(), it pushes the instructions specified within the setTimeout() to an Event loop and proceeds to execute instructions following the setTimeout() block (in the call stack). One the timeout duration elapses, the Callback Queue picks the instructions in the setTimeout puts them back on to the Call stack.

Syntax

setTimeout( // non-blocking
  function() {
    /* Execute this block after some MilliSeconds */
  } /*, duration_in_miliseconds */
);

Input Parameters

  1. setTimeout accepts a callback function with a set of instructions
  2. Number of MilliSeconds that it needs to execute the instructions inside the function

Return

  1. Returns an Interval ID to uniquely identify the block that will be executed in a while

Note that I have used IIFE and ES6 syntax( ==> ) to present my Illustrations

click me for more information on IIFE

click me for more information on ES6 ==>

intervalId

Illustration - 1

(function() {
  var intervalId = setTimeout(() => { // non-blocking
    console.log(intervalId);  // blocking within setTimeout
    console.log("Illustration - 1"); // blocking  within setTimeout
  }, 4000); // executes above instructions after 4 seconds
})();

Take-Aways

  1. setTimeout() is utilised to execute the following statements after 1000 mili seconds / 1 second

    console.log(intervalId); console.log("Illustration - 1");

  2. Also an Identifier intervalID can be assigned as a return variable to Uniquely identify the setTimeout()

Illustration - 2

console.log("2.1 Hello"); // blocking
(function() { 
  setTimeout(() => { // non blocking
    console.log("2.3 Illustration - 2"); // executes immediately
  });
})();
console.log("2.2 Hello"); // blocking

Take-Aways

  1. We need not pass the milliseconds parameter in which case the block of code within setTimeout behaves like regular blocking instructions and will get executed without a wait.
  2. But since setTimeout is non-blocking in nature the subsequent blocking instructions post setTimeout() will still get precedence as shown above

clearInterval

If the wait is too long, and one wants to terminate the timeout, clearTimeout(intervalId) will stop/halt instructions in the block from getting executed

Illustration - 3

(function() {
  var intervalId = setTimeout(() => { // non-blocking
    console.log("Illustration - 3"); // will not get executed !!!
  }, 5000);
  clearTimeout(intervalId); // clears intervalId immediately
})();

Take-Aways

  1. Note that in the above illustration, clearTimeout() will immediately execute; as its blocking in nature

Scope

Illustration - 4

(function() {
  var glob = "I am everywhere"; // blocking
  console.log("4.1 : ", glob); // blocking
  setTimeout(() => { // non-blocking
    console.log("4.3 : ", glob); // blocking within setTimeout
  }, 2000);
  console.log("4.2 : ", glob); // blocking
})();

Take-Aways

  1. Since setTimeout is nonblocking instruction to the interpreter, its pushed to Event Loop and the next instruction in the call stack (which logs 4.2..) will be logged
  2. Once the wait is over, which is 2 seconds, instruction within setTimeout block will be picked from callback queue and gets executed

Illustration - 5

(function() {
  for (var i = 0; i <= 2; i++) { // blocking
    setTimeout(() => { // non-blocking
      console.log("5.2 : ", i); // blocking within setTimeout, 5.2 :3
    }, 2000);
  }
  console.log("5.1 : ", i); // blocking, 5.1 :3
})();

Take-Aways

  1. In this Illustration, for-loop statements and console.log('5.2..') are blocking statements and hence will get executed first.

var i = 0; i <= 2; i++; console.log("5.2 : ", i);

  1. For each iteration, the setTimeout instruction console.log("5.2 : ", i); will be pushed to the Event Loop and loop counter "i" in the Call Stack keeps incrementing until it reaches 3
  2. Post which, the next subsequent blocking instruction will be picked (i.e console.log("5.1 : ", i);). At this point, variable "i" holds a value of 3 in it and hence gets printed 5.1 :3.
  3. Post the timeout duration, setTimeout instruction (non-blocking) console.log("5.2 : ", i); gets picked and finds that the current value of i in the Call Stack is 3 and logs the same on to the console

Illustration - 6

(function() {
  let i = 0; // blocking
  while (i <= 3) { // blocking
    setTimeout(() => { // non-blocking
      console.log("6.2 : ", i); // blocking within setTimeout (prints 6.2 :4 (thrice))
    }, 2000);
    i++; // blocking
  }
  console.log("6.1 : ", i); // blocking (prints 6.1 :4)
})();

Take-Aways

  1. Scoping is similar to var
  2. Picks the latest value from call stack

Illustration - 7

(function() {
  let wc = "hello"; // 1: blocking
  let name = ""; // blocking
  setTimeout(() => { // non-blocking
    name = "Sharu"; // blocking within setTimeout 
    console.log("7.2 : " + wc + " " +name); // blocking within setTimeout  (7.2: hello Sharu)
  }, 2000);
  console.log("7.1 : ", wc + name); // 3: blocking, (7.1 hello)
})();

Take-Aways

  1. Post setTimeout(), subsequent instructions does not wait for setTimeout() instructions(callback) to complete. 2. The Call Stack picks instructions specified after setTimeout() and executes them; as, the instructions within setTimeout() wait for the specified duration in the Event Loop
  2. In order to execute instructions after the specified duration, one should write them within the setTimeout() block only
  3. Instructions within the setTimeout() will execute instructions - within them - in a blocking way

Callback hell

Illustration - 8

(function() {
  alert("8.1 Welcome to XYZ"); // 1. Blocking
  setTimeout(() => { // Non Blocking
    var name = prompt("8.2 Please enter your Name : "); // 2. Blocking within setTimeout
    setTimeout(() => { // Non Blocking 
      alert("8.3 Welcome " + name); // 2. Non Blocking
    }, 1000); // 1 second
  }, 1000); // 1 second
})();

Take-Aways

  1. A chain of callbacks with setTimeout can be achieved as shown above, as the inner setTimeout gets blocked by its parent. Once the parent setTimeout completes, the inner setTimeout is pushed to Event Loop
  2. But invoking them repeatedly in this fashion makes it very difficult to read, debug them and hence becomes difficult to manage in the long run
  3. This is called commonly termed as "Callback hell"
  4. There are mechanisms to avoid this using Async() and Await()

There are indeed limits for setTimeout function Illustrated as shown below. setTimeout accepts a Signed Integer in milliseconds

Range [ 1 - 2^31 ]

(function() {
  setTimeout(() => {
    console.log("Hello !!!");
  }, 0);

  setTimeout(() => {
    console.log("I am ~ [24.85 Days -1 Millisecond] away");
  }, Math.floor(2 ** 31 - 1));

  setTimeout(() => {
    console.log("I am ~ [24.85 Days] away");
  }, Math.floor(2 ** 31));

  setTimeout(() => {
    console.log("I am ~ [24.85 Days + 1 Millisecond] away");
  }, Math.floor(2 ** 31 + 1));

  setTimeout(() => {
    console.log("I am [1 Millisecond] away");
  }, 1);

  setTimeout(() => {
    console.log("GoodBye !!!");
  }, 0);

  // Output:
  //   Hello !!!
  //   I am ~ [24.85 Days] away
  //   I am ~ [24.85 Days + 1 Millisecond] away
  //   I am [1 Millisecond] away
  //   GoodBye !!!
})();

Take-Aways:

  1. Anything on-or-above 2^31 is defaulted to 1 Millisecond and hence the behaviour
  2. Anything between 1 Millisecond and 2^31 (not inclusive) will go into a callback and wait for their set duration to resume execution
  3. As shown above, as 1 millisecond is such a short duration that its almost equivalent to 0 Milliseconds and hence the callback instructions within that setTimeout gets printed even before setTimeout() with 0 Milliseconds (GoodBye !!!) is consoled !!
  4. Note that this behaviour changes with browser-to-browser and one should test and verify in multiple browsers