Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions docs/01-getting-started/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,6 @@ Ready to dive deeper? Proceed to the next page to configure your local developme

```
docs/
├── 03-deep-dive-core/
│ ├── _category_.json
│ ├── closures-and-lexical-scope.md
│ ├── prototype-and-inheritance.md
│ ├── execution-context-and-callstack.md
│ └── this-keyword-explained.md
├── 04-asynchronous-javascript/
│ ├── _category_.json
│ ├── event-loop-and-task-queue.md
Expand Down
6 changes: 3 additions & 3 deletions docs/02-javascript-fundamentals/variables-and-scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Variables declared outside any function or block belong to the global scope.

:::note

When you run the above code, the output is undefined because the variable `globalAppTitle` is declared in the global scope and is accessible inside the function `printTitle()`.
In the example above, `globalAppTitle` is declared in the global scope, making it accessible from anywhere in the script, including inside functions like `printTitle()`.

So, when we call `printTitle()`, it logs the value of `globalAppTitle` to the console.

Expand Down Expand Up @@ -107,9 +107,9 @@ Try editing the script below to observe how block scope and variable re-assignme

## Best Practices

1. **Default to `const**`: Protect variables from unintended re-assignments.
1. **Default to `const`**: Protect variables from unintended re-assignments.
2. **Use `let` for reassignment**: Use only when values must mutate (e.g., loop counters, accumulators).
3. **Avoid `var**`: Prevent scope-leakage issues and silent hoisting bugs in modern applications.
3. **Avoid `var`**: Prevent scope-leakage issues and silent hoisting bugs in modern applications.

## Knowledge Check

Expand Down
119 changes: 119 additions & 0 deletions docs/04-asynchronous-javascript/event-loop-and-task-queue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
id: event-loop-and-task-queue
title: "The Event Loop & Task Queue Mechanics"
sidebar_label: Event Loop & Task Queues
sidebar_position: 1
description: "Master the JavaScript Event Loop, asynchronous execution concurrency, Call Stack, Microtask Queue, Macrotask Queue, and microtask starvation."
tags: [javascript, async, event-loop, microtasks, macrotasks, concurrency]
keywords: [javascript, async, event-loop, microtasks, macrotasks, concurrency]
---

import JSEditor from "@site/src/components/js-live-code-editor";
import CodeBlock from "@theme/CodeBlock";
import firstExample from "!!raw-loader!../_scripts/asynchronous-javascript/event-loop-playground.js";
import secondExample from "!!raw-loader!../_scripts/asynchronous-javascript/event-loop-exercise.js";

JavaScript is single-threaded, meaning it can only execute one line of code at a time on its main thread. Despite this limitation, it handles non-blocking asynchronous operations—such as network requests, file I/O, and timers—efficiently through the **Event Loop**.

## Architecture of Asynchronous Execution

To handle asynchronous tasks without freezing the user interface, the JavaScript engine works alongside browser Web APIs (or Node.js C++ bindings) and two distinct execution queues.

```text
JavaScript Asynchronous Architecture
┌─────────────────────────────────────────────────────────────┐
│ Call Stack (Synchronous Execution LIFO) │
└──────────────────────────────┬──────────────────────────────┘
│ Delegation
┌─────────────────────────────────────────────────────────────┐
│ Web APIs (Timers, Fetch HTTP, DOM Events, File Systems) │
└──────────────────────────────┬──────────────────────────────┘
│ Callbacks Ready
┌──────────────────────────────┬──────────────────────────────┐
│ Microtask Queue (High-Priority)│ Macrotask Queue (Task Queue)
│ ├── Promises (.then/catch) │ ├── setTimeout / setInterval │
│ ├── queueMicrotask() │ ├── setImmediate (Node.js) │
│ └── MutationObserver │ └── requestAnimationFrame │
└──────────────┬───────────────┴──────────────┬───────────────┘
│ │
└───────────────┬──────────────┘
│ Event Loop Tick
┌─────────────────────────────────────────────────────────────┐
│ Call Stack (Executed when Call Stack is empty) │
└─────────────────────────────────────────────────────────────┘

```

## Microtasks vs. Macrotasks (Task Priorities)

The Event Loop continuously monitors the **Call Stack**. When the stack is empty, it processes pending callbacks according to a strict priority hierarchy:

| Queue Type | Operations | Execution Rule / Priority |
| --- | --- | --- |
| **Microtask Queue** | `Promise` callbacks, `queueMicrotask()`, `process.nextTick` (Node) | **Highest Priority**: Drained **completely** until empty before any macrotask runs. |
| **Macrotask Queue** | `setTimeout`, `setInterval`, `setImmediate`, I/O, UI Rendering | **Normal Priority**: Processes **one single task** per Event Loop tick, then checks microtasks again. |

## The Event Loop Execution Algorithm

During every cycle ("tick") of the Event Loop, the runtime follows these exact steps:

1. **Execute Synchronous Code**: Process all frames in the Call Stack until it is completely empty.
2. **Drain Microtask Queue**: Process every single microtask currently in the queue. If a microtask schedules another microtask, it is executed in the *same* cycle.
3. **Render UI (Browser Only)**: Perform DOM re-paints and run `requestAnimationFrame` callbacks if needed.
4. **Execute One Macrotask**: Dequeue and run the oldest single task from the Macrotask Queue.
5. **Repeat**: Loop back to step 1.

## Microtask Starvation

Because the Event Loop must drain the **entire** Microtask Queue before yielding execution to macrotasks or UI rendering, continuously enqueuing microtasks will block the main thread indefinitely:

```javascript title="Infinite Microtask Starvation Example"
// WARNING: This recursively starves the Macrotask Queue and freezes the UI!
function infiniteMicrotask() {
Promise.resolve().then(() => {
infiniteMicrotask(); // Enqueues another microtask infinitely
});
}

// infiniteMicrotask(); // Un-commenting will crash/freeze the thread!

```

## Interactive Playground: Tracing Execution Order

Predict the log sequence of synchronous statements, `setTimeout`, and `Promise.then` callbacks:

<JSEditor title="Event Loop Playground" run={true}>
{firstExample}
</JSEditor>

## Best Practices

1. **Use Promises/Microtasks for Instant State Updates**: When you need async operations to resolve immediately before DOM repainting or downstream state changes occurs.
2. **Offload Heavy Loops with `setTimeout`**: Yield execution back to the browser frame engine by breaking intensive computations into macrotask chunks (`setTimeout(fn, 0)`).
3. **Avoid Infinite Microtask Chaining**: Never recursively queue microtasks (`queueMicrotask` or `.then`) without exit conditions; doing so blocks DOM rendering and input events.


## Knowledge Check

### Exercise Requirements:

Determine the exact console output order for the following code snippet:

<JSEditor title="Event Loop Exercise" run={true}>
{secondExample}
</JSEditor>

#### Explanation:

1. Synchronous execution logs `A` and `F`.
2. Microtask Queue processes `C` and `E`.
3. Inside microtask `C`, a new macrotask (`D`) is scheduled behind `B`.
4. Macrotask Queue runs `B` first (oldest macrotask), then runs `D`.

:::success Next Up
Now that you have mastered Event Loop ordering and task queues, proceed to **Promises and Async/Await**!
:::
162 changes: 162 additions & 0 deletions docs/04-asynchronous-javascript/fetch-api-and-ajax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
---
id: fetch-api-and-ajax
title: "Fetch API & Asynchronous HTTP"
sidebar_label: Fetch API & AJAX
sidebar_position: 3
description: "Master asynchronous HTTP requests in JavaScript with the Fetch API, AbortController timeout handling, streaming responses, and proper error handling."
tags: [javascript, async, fetch, ajax, http, abortcontroller, web-apis]
keywords: [javascript, async, fetch, ajax, http, abortcontroller, web-apis]
---

import JSEditor from "@site/src/components/js-live-code-editor";
import CodeBlock from "@theme/CodeBlock";
import firstExample from "!!raw-loader!../_scripts/asynchronous-javascript/live-fetch.js";

Asynchronous JavaScript and XML (**AJAX**) allows web applications to communicate with backend servers asynchronously without reloading the current page. Modern JavaScript applications utilize the native promise-based **Fetch API** for networking.

## Evolution: `XMLHttpRequest` to `fetch()`

Before `fetch()`, asynchronous requests relied on `XMLHttpRequest` (XHR), which required verbose, callback-driven code:

```javascript title="legacy-xhr.js"
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data");
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
console.log(JSON.parse(xhr.responseText));
}
};
xhr.onerror = function () {
console.error("Network Error");
};
xhr.send();
```

The modern **Fetch API** streamlines networking with standard Promises and cleaner configuration objects:

```javascript title="modern-fetch.js"
// Modern Promise-based Fetch
fetch("https://api.example.com/data")
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error("Fetch Error:", error));
```

## Configuring Requests & Headers

The `fetch()` function accepts two arguments: the target `resource` URL and an optional `options` configuration object.

```javascript title="fetch-config.js"
async function createUser(userData) {
const response = await fetch("https://api.example.com/users", {
method: "POST", // HTTP Method: GET, POST, PUT, DELETE, PATCH
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_JWT_TOKEN"
},
body: JSON.stringify(userData), // Serialize JavaScript object to JSON
mode: "cors", // cors, no-cors, or same-origin
cache: "no-cache"
});

return await response.json();
}

```

## The Fetch Error-Handling Gotcha

Unlike HTTP libraries like Axios, `fetch()` **does not reject** its Promise on HTTP error status codes (such as `404 Not Found` or `500 Internal Server Error`). A `fetch()` Promise only rejects on network failures or blocked requests.

To handle HTTP errors properly, check the `response.ok` boolean property (`status` between 200–299):

```javascript title="safe-fetch.js"
async function safeFetch(url) {
try {
const response = await fetch(url);

// Check for HTTP error status codes (4xx, 5xx)
if (!response.ok) {
throw new Error(`HTTP Error! Status: ${response.status} ${response.statusText}`);
}

const data = await response.json();
return data;
} catch (error) {
console.error("Request Failed:", error.message);
}
}

```

## Aborting Requests & Timeouts (`AbortController`)

To cancel ongoing network requests or implement request timeouts, use the native **`AbortController`** interface:

```javascript title="abort-fetch.js"
async function fetchWithTimeout(resource, options = {}) {
const { timeout = 5000 } = options;

// 1. Create an AbortController instance
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);

try {
const response = await fetch(resource, {
...options,
signal: controller.signal // Connect signal to fetch
});
clearTimeout(id);
return await response.json();
} catch (error) {
if (error.name === "AbortError") {
throw new Error("Request timed out!");
}
throw error;
}
}

```

## Interactive Playground: Live Fetch Explorer

Run the live `fetch()` request below using JSONPlaceholder API:

<JSEditor title="Live Fetch Playground" run={true}>
{firstExample}
</JSEditor>

## Best Practices

1. **Always Check `response.ok`**: Never assume a resolved `fetch()` promise indicates a successful data payload.
2. **Set Request Timeouts**: Wrap network calls with `AbortController` to prevent hanging pending requests on poor connections.
3. **Use Typed Response Parsing**: Match body parsing methods to payloads (`.json()`, `.text()`, `.blob()`, or `.arrayBuffer()`).

## Knowledge Check

### Exercise Requirements:

Write a reusable function `postJSON(url, payload)` using `async/await` that sends a `POST` request, handles non-2xx HTTP errors properly, and returns the parsed JSON response.

```javascript title="solution.js"
async function postJSON(url, payload) {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
});

if (!response.ok) {
throw new Error(`Failed to POST to ${url} - Status: ${response.status}`);
}

return await response.json();
}

```

:::success Phase 04 Complete!
Congratulations! You have completed **Phase 04: Asynchronous JavaScript**. Proceed to **Phase 05: DOM & Browser APIs** to build interactive Web applications!
:::
Loading
Loading