Skip to main content

📘 JavaScript Advanced Interview Notes

Deep Async, Event Loop, Tricky Outputs & Machine Coding

Table of Contents

Part 1: Deep Async JavaScript & Event Loop

  1. JavaScript Is Single-Threaded — Then How Is Async Possible?
  2. Runtime Architecture
  3. Event Loop Priority Order
  4. Microtasks vs Macrotasks
  5. Deep Event Loop Example
  6. Nested Microtasks
  7. async / await Internals
  8. await vs then
  9. Blocking the Event Loop

Part 2: Output-Based Tricky JavaScript Questions

  1. var + Closure
  2. let vs var Hoisting
  3. this Confusion
  4. Arrow Function this Trap
  5. Object Mutation
  6. Object Keys Stringification
  7. == Weirdness
  8. Function Hoisting Priority

Part 3: JavaScript Machine Coding Questions

  1. Debounce Function
  2. Throttle Function
  3. Polyfill for Promise.all
  4. Polyfill for bind
  5. Flatten Nested Array
  6. Deep Clone Object
  7. Once Function
  8. Implement Event Emitter
  9. Retry Promise Function
  10. Implement sleep

1. Deep Async JavaScript & Event Loop (INTERNALS)


1.1 JavaScript Is Single-Threaded — Then How Is Async Possible?

JavaScript has:

  • Single Call Stack
  • Asynchronous runtime environment

Async behavior is handled by:

  • Web APIs
  • Task queues
  • Event Loop

1.2 Runtime Architecture (Mental Diagram)

┌──────────────┐
│ Call Stack │
└──────┬───────┘

┌──────▼───────┐
│ Web APIs │ (setTimeout, fetch, DOM events)
└──────┬───────┘

┌──────▼──────────┐
│ Task Queues │
│ - Microtasks │ (Promises, queueMicrotask)
│ - Macrotasks │ (setTimeout, setInterval)
└──────┬──────────┘

┌──────▼───────┐
│ Event Loop │
└──────────────┘

1.3 Event Loop Priority Order (CRITICAL)

Execution order:

  1. Call Stack
  2. Microtask Queue
  3. Macrotask Queue

Microtasks always execute before macrotasks


1.4 Microtasks vs Macrotasks

Microtasks

  • Promise.then
  • Promise.catch
  • Promise.finally
  • queueMicrotask
  • MutationObserver

Macrotasks

  • setTimeout
  • setInterval
  • setImmediate (Node)
  • requestAnimationFrame

1.5 Deep Event Loop Example (Most Asked)

console.log("A");

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

Promise.resolve().then(() => {
console.log("C");
});

console.log("D");

Execution Breakdown

  1. A → Call stack
  2. setTimeout → Web API
  3. Promise.then → Microtask queue
  4. D → Call stack empty
  5. Microtask → C
  6. Macrotask → B

Output

A
D
C
B

1.6 Nested Microtasks (Very Tricky)

Promise.resolve().then(() => {
console.log("A");
Promise.resolve().then(() => {
console.log("B");
});
});

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

Output

A
B
C

Reason:

  • Microtasks can schedule more microtasks
  • All microtasks must finish before macrotasks

1.7 async / await Internals (IMPORTANT)

async function test() {
console.log("A");
await Promise.resolve();
console.log("B");
}

console.log("C");
test();
console.log("D");

How it works internally

  • await pauses function execution
  • Remaining code goes to microtask queue

Output

C
A
D
B

1.8 await vs then (Same Priority)

await Promise.resolve();
Promise.resolve().then();

Both go to microtask queue.


1.9 Blocking the Event Loop (Interview Trap)

setTimeout(() => console.log("A"), 0);

for (let i = 0; i < 1e10; i++) {}

console.log("B");

Output

B
A

Why:

  • Call stack is blocked
  • Event loop cannot push tasks

2. Output-Based Tricky JavaScript Questions


2.1 var + Closure (Classic)

for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}

Output

3
3
3

Fix

for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0);
}

2.2 let vs var Hoisting

console.log(a);
var a = 10;

console.log(b);
let b = 20;

Output

undefined
ReferenceError

2.3 this Confusion

const obj = {
name: "JS",
getName: function () {
return this.name;
},
};

const fn = obj.getName;
console.log(fn());

Output

undefined

Reason:

  • Lost context
  • this → global / undefined (strict)

2.4 Arrow Function this Trap

const obj = {
name: "JS",
greet: () => {
console.log(this.name);
},
};

obj.greet();

Output

undefined

Arrow functions do not bind this


2.5 Object Mutation

const obj = { a: 1 };
const obj2 = obj;

obj2.a = 2;
console.log(obj.a);

Output

2

2.6 Object Keys Stringification

const obj = {};
obj[1] = "one";
obj["1"] = "one again";

console.log(obj);

Output

{ "1": "one again" }

2.7 == Weirdness

[] == ![];

Output

true

Explanation:

  • ![] → false
  • [] == false"" == 0 → true

2.8 Function Hoisting Priority

foo();

var foo = function () {
console.log("A");
};

function foo() {
console.log("B");
}

Output

B

Function declarations beat var


3. JavaScript Machine Coding Questions (INTERVIEW LEVEL)


3.1 Debounce Function

function debounce(fn, delay) {
let timer;

return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}

Use cases:

  • Search input
  • Resize events

3.2 Throttle Function

function throttle(fn, limit) {
let lastCall = 0;

return function (...args) {
const now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
fn.apply(this, args);
}
};
}

Use cases:

  • Scroll events
  • Infinite scroll

3.3 Polyfill for Promise.all

Promise.myAll = function (promises) {
return new Promise((resolve, reject) => {
const results = [];
let completed = 0;

promises.forEach((p, i) => {
Promise.resolve(p)
.then((res) => {
results[i] = res;
completed++;
if (completed === promises.length) {
resolve(results);
}
})
.catch(reject);
});
});
};

3.4 Polyfill for bind

Function.prototype.myBind = function (context, ...args) {
const fn = this;
return function (...innerArgs) {
return fn.apply(context, [...args, ...innerArgs]);
};
};

3.5 Flatten Nested Array

function flatten(arr) {
return arr.reduce((acc, val) => {
if (Array.isArray(val)) {
acc.push(...flatten(val));
} else {
acc.push(val);
}
return acc;
}, []);
}

3.6 Deep Clone Object

function deepClone(obj) {
if (obj === null || typeof obj !== "object") return obj;

const copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
copy[key] = deepClone(obj[key]);
}
return copy;
}

3.7 Once Function

function once(fn) {
let called = false;
let result;

return function (...args) {
if (!called) {
called = true;
result = fn.apply(this, args);
}
return result;
};
}

3.8 Implement Event Emitter

class EventEmitter {
constructor() {
this.events = {};
}

on(event, fn) {
this.events[event] = this.events[event] || [];
this.events[event].push(fn);
}

emit(event, data) {
(this.events[event] || []).forEach((fn) => fn(data));
}

off(event, fn) {
this.events[event] = (this.events[event] || []).filter((f) => f !== fn);
}
}

3.9 Retry Promise Function

function retry(fn, retries) {
return fn().catch((err) => {
if (retries === 0) throw err;
return retry(fn, retries - 1);
});
}

3.10 Implement sleep

function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}