0% found this document useful (0 votes)
8 views8 pages

Async JavaScript

This document is a comprehensive cheatsheet on asynchronous JavaScript, covering key concepts such as the event loop, promises, async/await, error handling, and concurrency patterns. It provides essential rules, methods, and common mistakes to avoid, along with practical examples and mental models to aid understanding. The document serves as a quick reference for developers to effectively manage asynchronous operations in JavaScript.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views8 pages

Async JavaScript

This document is a comprehensive cheatsheet on asynchronous JavaScript, covering key concepts such as the event loop, promises, async/await, error handling, and concurrency patterns. It provides essential rules, methods, and common mistakes to avoid, along with practical examples and mental models to aid understanding. The document serves as a quick reference for developers to effectively manage asynchronous operations in JavaScript.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

⚡ Async JavaScript

Master Cheatsheet
Event Loop • Promises • Async/Await • Error Handling • Patterns
Created by: Neeraj | LinkedIn: neeraj-kumar1904 💼 | X: @_19_neeraj 🐦 | GitHub: Neeraj05042001 🐙

1. THE EVENT LOOP — Foundation of Everything

Mental Model — The 4 Boxes


JavaScript is single-threaded. Only one thing runs at a time. The Event Loop decides WHAT runs WHEN.

Call Stack Web APIs Microtask Queue Callback Queue

Where your code runs Where setTimeout, Where Promises Where setTimeout
right now fetch, DOM events resolve (HIGH callbacks wait (LOW
live priority) priority)

Priority Rule — Most Important Thing to Remember


Microtask Queue (Promises) ALWAYS runs before Callback Queue (setTimeout)

Predict the Output — Test Your Understanding


[Link]('1'); // sync → runs immediately
setTimeout(() => [Link]('2'), 0); // callback queue → last
[Link]().then(() => [Link]('3'));// microtask queue → before setTimeout
[Link]('4'); // sync → runs immediately

// Output: 1, 4, 3, 2

The Loop Rule


When call stack is empty... Event loop checks microtask queue first,
then callback queue

Promise resolves Goes to microtask queue → runs BEFORE any


setTimeout

setTimeout fires Goes to callback queue → runs AFTER all


promises settle

await keyword Pauses current function, frees the call


stack for other work
2. PROMISES — The Building Block

Promise States — 3 and only 3


State Meaning What happens

Pending Still waiting .then() and .catch() wait

Fulfilled ✅ Resolved with a value .then() runs

Rejected ❌ Failed with an error .catch() runs

KEY: Once a Promise settles (fulfilled or rejected) it can NEVER change state again

Chaining — Every .then() returns a NEW Promise


[Link](1)
.then(x => x + 1) // x=1, returns 2
.then(x => x * 2) // x=2, returns 4
.then(x => [Link](x)); // 4

// Errors skip .then() and jump to .catch()


[Link](new Error('boom'))
.then(x => x + 1) // SKIPPED
.then(x => x * 2) // SKIPPED
.catch(err => [Link]([Link])); // 'boom'

Key Methods Reference


.then(onSuccess) Runs when promise resolves. Returns a new promise.

.catch(onError) Runs when promise rejects. Same as .then(null, onError).

.finally(fn) Runs on BOTH resolve and reject. Good for cleanup — but
has timing gotchas.

.then(onSuccess, onError) Handle both in one call. Safer timing than chaining
.catch() separately.

Sharing a Promise — The Key Insight


// A Promise is just an OBJECT. Multiple callers can .then() the same one.
const sharedPromise = fetch('/api/data'); // ONE network call

[Link](r => [Link]('Caller A:', r));


[Link](r => [Link]('Caller B:', r));
[Link](r => [Link]('Caller C:', r));
// All 3 get the same result. Only 1 request was made.

3. ASYNC / AWAIT — Promise Sugar


The Golden Rule
async/await is just Promise syntax. Every async function returns a Promise. await unwraps
it.

Side-by-Side Comparison
// Promise version // async/await version
function getData() { async function getData() {
return fetch('/api') const res = await fetch('/api');
.then(res => [Link]()) const data = await [Link]();
.then(data => [Link]); return [Link];
} }
// Both are IDENTICAL under the hood

Critical Traps
❌ WRONG (Sequential — slow) ✅ RIGHT (Parallel — fast)

for (const id of ids) { await fetch(id); await [Link]( [Link](id =>


// waits each time } fetch(id)) );

// await pauses only the CURRENT function, not the whole program
// Other async operations continue running while you await

// async function ALWAYS returns a Promise even if you return a plain value
async function greet() { return 'hello'; }
greet(); // → Promise<'hello'>, not just 'hello'

4. ERROR HANDLING — The Part Everyone Gets Wrong

How Errors Travel


// Error propagates down the chain, skipping .then(), until caught
asyncOperation() // throws/rejects
.then(doStepA) // SKIPPED
.then(doStepB) // SKIPPED
.catch(handleError) // ← error arrives here
.then(doStepC); // runs again after catch (catch returns resolved promise)

Cleanup — .finally() vs .then() + .catch()


.finally() Runs after resolve OR reject. Simple cleanup. But has
microtask TIMING ISSUES in some cases (avoid for cache
cleanup).

.then(fn, fn) Handles both in same microtask tick. Use when cleanup timing
matters (like deleting from a Map/cache).
try { } catch { } Async/await equivalent of the above. finally block always
finally { } runs.

Why Delete in BOTH .then() and .catch()


// If you only cleanup on SUCCESS:
// → failed request stays in cache forever → callers can never retry

// If you only cleanup on FAILURE:


// → successful request stays in cache → callers get stale data forever

// ✅ Always cleanup in BOTH:


[Link](
(result) => { [Link](key); return result; }, // success: cleanup + pass value
(err) => { [Link](key); throw err; } // failure: cleanup + re-throw
);

Unhandled Rejection — [Link] Crashes


Any rejected promise with no .catch() will CRASH [Link]. Always handle errors.

// Add a .catch(() => {}) on side-chains to prevent crash


// while still letting the RETURNED promise carry the real error to the caller
const promise = apiCall(id);
[Link](() => {}); // suppress crash on side-chain
return promise; // caller still gets the real rejection

5. CONCURRENCY PATTERNS — The Toolkit

Promise Combinators — Which one to use?


Method Behavior Use when...

[Link]([]) All must succeed. Fails fast You need ALL results and can't
if ANY rejects. proceed if any fail

[Link]([]) Runs all. Never fails. You want all results regardless
Returns status for each. of failures

[Link]([]) First to settle wins (resolve Timeout logic — race fetch vs


OR reject). sleep(5000)

[Link]([]) First to SUCCEED wins. Fallbacks — try multiple


Ignores rejections. sources, use first success

Pattern: Deduplication — Share In-Flight Promises


Trigger: Multiple callers want the same async result at the same time

const cache = new Map();


function deduped(id, apiCall) {
if ([Link](id)) return [Link](id); // return SAME promise

const promise = apiCall(id)


.then(result => { [Link](id); return result; }) // cleanup both
.catch(err => { [Link](id); throw err; }); // cases!

[Link](id, promise);
return promise;
}

Pattern: Async Recursion — Follow a Chain


Trigger: Response tells you to fetch again with a different ID (redirects, pagination,
trees)

async function fetchWithRedirect(id, fetcher, depth = 0, maxDepth = 5) {


if (depth > maxDepth) throw new Error('Max redirect depth exceeded');

const response = await fetcher(id);

if ([Link]) {
return fetchWithRedirect([Link], fetcher, depth + 1, maxDepth);
}

return response; // base case — no redirect, we're done


}

Pattern: Parallel + Redirect (combining patterns)


Trigger: Array/object of IDs, each may redirect, all should run at the same time

async function fetchDeep(ids, fetcher, maxDepth = 5) {


async function fetchOne(id, depth) {
if (depth > maxDepth) throw new Error('Max redirect depth exceeded');
const res = await fetcher(id);
if ([Link]) return fetchOne([Link], depth + 1);
return res;
}

const keys = [Link](ids);


const results = await [Link]([Link](k => fetchOne(ids[k], 0)));

return [Link]([Link]((k, i) => [k, results[i]]));


}

Pattern: Retry with Backoff


Trigger: Network call may fail temporarily, try again automatically

async function withRetry(fn, retries = 3, delayMs = 100) {


try {
return await fn();
} catch (err) {
if (retries === 0) throw err;
await new Promise(r => setTimeout(r, delayMs));
return withRetry(fn, retries - 1, delayMs * 2); // exponential backoff
}
}

Pattern: Timeout
Trigger: You want to fail fast if a request takes too long

function withTimeout(promise, ms) {


const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timed out')), ms)
);
return [Link]([promise, timeout]); // first to settle wins
}

6. PATTERN RECOGNITION — See the Problem, Know the Tool

The 6 Questions to Ask on Every Problem


Question Answer Reach for

Multiple callers want the same thing at Yes Deduplication (Map + shared
the same time? Promise)

Does each step depend on the result of Yes Async Recursion or


the previous? sequential await

Can things run at the same time Yes [Link]([Link](...))


independently?

Can it run forever / loop back on Yes Depth counter OR visited Set
itself?

Does it need to fail fast if too slow? Yes [Link]([fetch,


timeout])

Can it fail temporarily but succeed on Yes Retry with exponential


retry? backoff

Data Structure Quick Pick


Map Cache: key → in-flight Promise. Deduplication, memoization. Use
.has(), .get(), .set(), .delete()

Set Track visited IDs. Loop detection. O(1) lookup with .has()

Array Collect promises for [Link](). [Link](fn) → array of


promises

Object Build structured output: { key: result } — use [Link]() +


[Link]()
7. COMMON MISTAKES — What Will Bite You

Mistake Symptom Fix

await inside a regular for Everything runs Use [Link] + .map()


loop sequentially (slow)

Not passing id to apiCall() Returns 'data-undefined' apiCall(id) not apiCall()

Cache inside the function Cache resets every call — Cache must live OUTSIDE the
dedup never works function

Storing result, not Promise Duplicates fire before Store the Promise object
result ready itself

.finally() for cache Timing issues — cache not Use .then(fn, fn) for explicit
cleanup cleared in time cleanup

Unhandled promise rejection [Link] crashes with Add .catch() on all promise
'Error: boom' chains

No base case in recursion Stack overflow / infinite Always check: if (!redirectId)


loop return

Forgetting to re-throw in Error swallowed, caller Always: catch(err => {


.catch() thinks it succeeded cleanup; throw err; })

8. MENTAL MODELS — Think in Pictures

The Restaurant Analogy


// JavaScript = one waiter (single thread)
// Kitchen = Web APIs (setTimeout, fetch — work happens here in background)
// Order tickets = Microtask queue (Promise callbacks — handled first)
// Walk-in requests = Callback queue (setTimeout — handled after tickets)

// Waiter takes order → gives to kitchen → serves OTHER tables while waiting
// Kitchen calls back → waiter finishes current table → picks up that order

Promise Chain = Assembly Line


fetch('/api') // Station 1: get raw response
.then(r => [Link]()) // Station 2: parse to JSON
.then(d => [Link]) // Station 3: extract users array
.catch(handleError) // Quality control: catch any defects from ANY station
.finally(cleanup); // Cleanup: always runs at the end

Recursion = Russian Dolls


// Each doll (call) opens the next one until you hit the smallest (base case)
fetchWithRedirect('id-1') // opens → redirects to id-2
fetchWithRedirect('id-2') // opens → redirects to id-3
fetchWithRedirect('id-3') // opens → NO redirect → return data
// data flows back out through each doll
[Link] = Parallel Kitchen Stations
// All stations start cooking AT THE SAME TIME
// You wait until the LAST one is ready
// If ANY station fails → whole order fails

await [Link]([
fetch('/api/users'), // starts immediately
fetch('/api/products'), // starts immediately
fetch('/api/orders'), // starts immediately
]); // wait for all 3 to finish

Async JavaScript Master Cheatsheet • Built from real problems, real bugs, real fixes

You might also like