JavaScript + Node.js track

Lesson 14 · Part 4: Async

Callbacks & Timers

`setTimeout`, the event loop and callback hell

Part 4: Async2 min readFree lesson

Level: Intermediate | Player: 13_Higher-order functions

Learning Objectives

  • Understand the difference between synchronous and asynchronous.
  • Use setTimeout, setInterval, clearTimeout, and clearInterval.
  • Understand the concept of the event loop as a big picture.
  • I recognize why callback hell is a problem and feel the need for the next section (Promise).
  • Prevent resource leaks by managing timer IDs.

1. Synchronous vs. asynchronous

Synchronous code is executed line by line, with one task completing before moving on to the next. Asynchronous code schedules “something to run later” and immediately moves on to the next code.

console.log("A");
setTimeout(() => console.log("B"), 0);
console.log("C");
//Output: A, C, B

Even setTimeout(fn, 0) runs after all synchronous code has finished.

2. setTimeout and clearTimeout

setTimeout(fn, ms) executes the callback once after the specified number of milliseconds. The return value is the timer ID, and can be canceled with clearTimeout(id).

const id = setTimeout(() => console.log("not running"), 1000);
clearTimeout(id);

ms is "minimum latency", not "exact time". If the main thread is busy, it will run slower.

3. setInterval and clearInterval

setInterval(fn, ms) repeats callbacks at regular intervals. It must be disposed of as clearInterval when it is no longer needed.

let n = 0;
const id = setInterval(() => {
  n += 1;
  if (n >= 3) clearInterval(id);
  console.log(n);
}, 500);

4. Event loop (big picture)

JavaScript is single-threaded, but the Timer/Network API provided by the host environment (Browser/Node) operates separately. Callbacks are queued and executed one by one when the call stack is empty. More details are covered in Lesson 16 and beyond.

5. Callback Hell

If asynchronous operations are required in succession, callbacks are placed within callbacks, deepening the indentation.

step1((a) => {
  step2(a, (b) => {
    step3(b, (c) => {
      console.log(c);
    });
  });
});

To solve this problem, learn Promise in Unit 15 and async/await in Unit 16.

Core example

file Contents covered
01_settimeout.js setTimeout/clearTimeout default
02_setinterval.js Repeat with setInterval + termination condition
03_event_loop.js Observe synchronous/asynchronous execution order
04_callback_hell.js Demonstrating the problem of callback nesting

⚠️ Common Mistakes

  1. Immediately call the function (in parentheses) like setTimeout(fn(), 1000) and pass the result.
  2. Leak occurred because setInterval was created and clearInterval was forgotten.
  3. Expect setTimeout(fn, 0) to run immediately.
  4. Attempting to catch an error thrown within an asynchronous callback with an external try/catch.
  5. Using this inside the callback without knowing that it is different from the outside (resolved with arrow function).

❓ Frequently Asked Questions (FAQ)

Q1. What is the difference between setTimeout 0ms and immediate execution? A. 0ms also goes through the queue once, so it is executed after all current synchronous code is finished.

Q2. Difference between setInterval and recursive setTimeout? A. setInterval tries to maintain the interval regardless of the callback execution time, and recursive setTimeout sets the next timer after the callback ends.

Q3. Are the timers in the browser and Node the same? A. The API signature is the same, but details such as minimum delay time and priority queue are different.

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/01_settimeout.jsJavaScript
/**
 *setTimeout basic usage.
 *A message is output after 1 second, and another timer is canceled immediately.
 */
console.log("start");

setTimeout(() => {
  console.log("Executed after 1 second");
}, 1000);

const cancelId = setTimeout(() => {
  console.log("This message is not output.");
}, 500);
clearTimeout(cancelId);

console.log("end (synchronous code)");
src/02_setinterval.jsJavaScript
/**
 *With setInterval, it counts every 0.5 seconds and stops after 3 times.
 */
let count = 0;
const intervalId = setInterval(() => {
  count += 1;
  console.log(`tick ${count}`);
  if (count >= 3) {
    clearInterval(intervalId);
    console.log("Cleaned up");
  }
}, 500);
src/03_event_loop.jsJavaScript
/**
 *Observe the execution order of synchronous code and asynchronous callbacks.
 *Expected output: A -> D -> C -> B
 */
console.log("A");

setTimeout(() => console.log("B"), 100);
setTimeout(() => console.log("C"), 0);

console.log("D");
src/04_callback_hell.jsJavaScript
/**
 *It shows a pattern of overlapping callbacks (callback hell).
 *Compare how this smooths out with Promises in the next section.
 */
function step(name, ms, callback) {
  setTimeout(() => {
    console.log(`${name} done`);
    callback();
  }, ms);
}

step("Step 1", 200, () => {
  step("Step 2", 200, () => {
    step("Step 3", 200, () => {
      console.log("Complete all steps");
    });
  });
});

Assignment

1. Countdown

Write the countdown(n) function. Outputs from n to 0 at 1 second intervals, and after outputting 0, “Fire!” Prints .

2. Sequential execution

Write code that executes three asynchronous operations one after the other with callbacks. Each task is simulated with setTimeout.

The reference solution is shown below.

Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

homework_01.jsJavaScript
/**
 *After outputting from n to 0 at 1 second intervals, “Fire!” Prints .
 *@param {number} n starting number
 */
function countdown(n) {
  let current = n;
  const id = setInterval(() => {
    if (current < 0) {
      clearInterval(id);
      console.log("Fire!");
      return;
    }
    console.log(current);
    current -= 1;
  }, 1000);
}

countdown(3);
homework_02.jsJavaScript
/**
 *Executes three asynchronous tasks sequentially.
 *@param {string} name task name
 *@param {Function} done completion callback
 */
function task(name, done) {
  setTimeout(() => {
    console.log(`${name} done`);
    done();
  }, 300);
}

task("A", () => {
  task("B", () => {
    task("C", () => {
      console.log("End all tasks");
    });
  });
});

Learn this lesson with a mentor

Work through the JavaScript + Node.js track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now