|

JavaScript 43 🧬 async/await

async function fetchData() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();
  return data;
}

fetchData().then(data => console.log(data));

async function run() {
  try {
    const result = await fetchData();
    console.log(result);
  } catch (err) {
    console.error('Failed:', err.message);
  } finally {
    console.log('Done');
  }
}

run();

const p1 = fetchData();
const p2 = fetchData();
const [a, b] = await Promise.all([p1, p2]);

async function sequential() {
  const a = await step1();
  const b = await step2(a);
  return step3(b);
}

async function parallel() {
  const [a, b] = await Promise.all([step1(), step2()]);
  return [a, b];
}

async and await are the syntax sugar on top of Promises that makes asynchronous code read like synchronous code. Instead of chaining .then() calls, you write await in front of a Promise — and the function pauses until the Promise settles, without blocking the rest of the program.

Key point: async marks a function as returning a Promise. await pauses inside that function until a Promise resolves, then returns the value — or throws if it rejects. The magic is that it doesn’t block the main thread. Other code keeps running while the awaited Promise is pending.


a – What is async/await

async and await are two keywords that go together. async before a function makes it return a Promise. await inside an async function pauses execution until a Promise settles.

The async keyword:

async function greet() {
  return 'Hello';
}

greet().then(v => console.log(v));
// [ Hello ]

Even though greet returns a string, the function wraps it in a resolved Promise. You can .then() it.

Equivalent without async:

function greet() {
  return Promise.resolve('Hello');
}

async is just sugar — it means “always return a Promise”.

The await keyword:

async function run() {
  const value = await Promise.resolve(42);
  console.log(value);
}

run();
// [ 42 ]

await waits for the Promise to resolve, then returns its value. If the Promise rejects, await throws the rejection reason.

What await really does:

┌──────────────────────────────────────────────┐
│  await somePromise                           │
│                                              │
│  1. Pause execution of the async function    │
│  2. Return control to the caller             │
│  3. Wait for the Promise to settle           │
│  4. Resume with the resolved value           │
│     OR throw the rejection reason            │
│                                              │
│  The rest of the program keeps running.      │
│  Only this function pauses.                  │
│                                              │
└──────────────────────────────────────────────┘

Key rules:

RuleMeaning
await only works inside async functionsElse syntax error
async functions always return a PromiseEven for plain returns
await pauses only the async functionNot the whole program
await throws on rejectionWrap in try/catch
await on non-Promise valuesWraps them and resolves immediately
Top-level awaitWorks in ES modules only

await works on any value:

async function run() {
  const a = await 42;              // wraps in Promise.resolve(42)
  const b = await Promise.resolve('hi');
  console.log(a, b);
}

run();
// [ 42 hi ]

You can await anything. If it’s a Promise, it waits. If it’s a plain value, it resolves immediately.

Async functions return Promises — always:

async function getValue() {
  return 42;
}

console.log(getValue());
// [ Promise { 42 } ]

getValue().then(v => console.log(v));
// [ 42 ]

Even a plain return in an async function gets wrapped in a Promise.

Async functions and thrown errors:

async function fails() {
  throw new Error('boom');
}

fails().catch(err => console.log(err.message));
// [ boom ]

A throw inside an async function rejects the returned Promise.

Comparing Promise chains to async/await:

// Promise chain
function loadUser() {
  return fetch('/user')
    .then(res => res.json())
    .then(user => fetch(`/orders/${user.id}`))
    .then(res => res.json())
    .catch(err => console.error(err));
}

// async/await
async function loadUser() {
  try {
    const res = await fetch('/user');
    const user = await res.json();
    const orderRes = await fetch(`/orders/${user.id}`);
    return await orderRes.json();
  } catch (err) {
    console.error(err);
  }
}

Both do the same thing. The async/await version reads top-to-bottom like sync code.

Why async/await matters:

  • Readability — no .then() pyramid
  • Error handlingtry/catch works like sync code
  • Debugging — stack traces point to the right line
  • Logic — conditionals, loops, and if/else work naturally
  • Adoption — every modern API returns Promises

When to use async/await vs .then():

SituationPreferred
Multi-step async logicasync/await
Complex error handlingasync/await
Simple one-off.then()
Combining PromisesPromise.all + await
Inside callbacksEither

In practice, async/await wins most of the time.


b – async/await syntax and usage

The syntax is straightforward once you know the rules. This section covers all the forms and patterns.

Basic async function:

async function doWork() {
  const result = await someAsyncTask();
  return result;
}

Async arrow function:

const doWork = async () => {
  const result = await someAsyncTask();
  return result;
};

Async method in an object:

const obj = {
  async doWork() {
    return await someAsyncTask();
  }
};

Async method in a class:

class Service {
  async fetchUser(id) {
    const res = await fetch(`/users/${id}`);
    return res.json();
  }
}

Async IIFE (immediately invoked function expression):

(async () => {
  const data = await fetchData();
  console.log(data);
})();

Useful for running async code at the top level of older scripts.

await on function calls:

async function main() {
  const user = await getUser(1);
  const orders = await getOrders(user.id);
  const details = await getDetails(orders[0].id);
  return details;
}

Each await pauses until the Promise settles.

await on Promise.all:

async function load() {
  const [user, orders, products] = await Promise.all([
    fetch('/user').then(r => r.json()),
    fetch('/orders').then(r => r.json()),
    fetch('/products').then(r => r.json())
  ]);
  return { user, orders, products };
}

All three run in parallel; await waits for all of them.

await on Promise.allSettled:

async function loadTolerant() {
  const results = await Promise.allSettled([a(), b(), c()]);
  return results.map(r => r.status === 'fulfilled' ? r.value : null);
}

Sequential vs parallel — the key decision:

// Sequential — each waits for the previous
async function sequential() {
  const a = await slowStep1();
  const b = await slowStep2();   // starts after step1
  const c = await slowStep3();   // starts after step2
  return [a, b, c];
}
// Total time: 1 + 2 + 3 = 6 seconds

// Parallel — all start at once
async function parallel() {
  const [a, b, c] = await Promise.all([
    slowStep1(),
    slowStep2(),
    slowStep3()
  ]);
  return [a, b, c];
}
// Total time: max(1, 2, 3) = 3 seconds

A common mistake — forgetting await:

async function wrong() {
  const data = fetch('/data');   // ❌ no await
  console.log(data);             // Promise, not data
}

async function right() {
  const data = await fetch('/data');   // ✅
  console.log(data);                    // Response
}

Without await, you get the Promise itself, not its resolved value.

Awaiting in a loop — the trap:

// ❌ Sequential — slow
for (const url of urls) {
  const data = await fetch(url);
  results.push(data);
}

// ✅ Parallel — fast
const results = await Promise.all(urls.map(u => fetch(u)));

If the operations are independent, don’t await inside a loop — use Promise.all.

await inside a for...of when order matters:

async function processInOrder(items) {
  for (const item of items) {
    await processItem(item);   // must be sequential
  }
}

Use for...of (not forEach) when you need sequential execution.

forEach does not await:

// ❌ Doesn't wait
items.forEach(async item => {
  await processItem(item);   // this runs later
});
console.log('Done');         // prints before items are processed

// ✅ Waits
for (const item of items) {
  await processItem(item);
}
console.log('Done');

forEach ignores the return value, so await inside it does nothing useful.

Returning values:

async function getValue() {
  const x = await Promise.resolve(10);
  return x * 2;   // wrapped in Promise.resolve(20)
}

getValue().then(v => console.log(v));
// [ 20 ]

Returning a Promise:

async function getValue() {
  return Promise.resolve(42);   // Promise is flattened
}

getValue().then(v => console.log(v));
// [ 42 ]

Async functions unwrap returned Promises — you get the resolved value.

await in conditions:

async function check() {
  if (await isLoggedIn()) {
    return await getProfile();
  }
  return null;
}

await works anywhere inside an async function — conditions, loops, expressions.

await in expressions:

async function sum() {
  const total = (await getA()) + (await getB());
  return total;
}

Top-level await (ES modules only):

// In an .mjs file or <script type="module">
const data = await fetch('/api').then(r => r.json());
console.log(data);

In Node.js, works in .mjs or package.json with "type": "module".

Async function returning nothing:

async function noReturn() {
  await something();
  // returns Promise<void>
}

noReturn().then(() => console.log('Done'));
// [ Done ]

Nested async functions:

async function outer() {
  const inner = async () => {
    return await fetch('/data');
  };
  return await inner();
}

Async functions can be nested freely.

The await precedence trap:

// ❌ Wrong — await binds to fetch(...).then(...)
const data = await fetch('/data').then(r => r.json());

// ✅ Same thing, but clearer
const res = await fetch('/data');
const data = await res.json();

await has low precedence but only applies to the immediately following expression. When in doubt, break it into two lines.

Complete example — fetching a user and their posts:

async function getUserWithPosts(id) {
  const userRes = await fetch(`/users/${id}`);
  const user = await userRes.json();

  const postsRes = await fetch(`/users/${id}/posts`);
  const posts = await postsRes.json();

  return { user, posts };
}

getUserWithPosts(1).then(({ user, posts }) => {
  console.log(user.name);
  console.log(posts.length);
});

c – Error handling with async/await

Async/await makes error handling simpler — you use plain try/catch, just like synchronous code.

await throws on rejection:

async function run() {
  try {
    const res = await fetch('/bad-url');
    const data = await res.json();
  } catch (err) {
    console.log('Caught:', err.message);
  }
}

run();
// [ Caught: Failed to fetch ]

Any rejected Promise — or any throw — becomes an exception inside the try block.

try/catch/finally all work:

async function run() {
  showSpinner();
  try {
    const data = await fetchData();
    return data;
  } catch (err) {
    console.error('Failed:', err.message);
    return null;
  } finally {
    hideSpinner();   // always runs
  }
}

Catching specific errors:

async function run() {
  try {
    const data = await fetchData();
  } catch (err) {
    if (err instanceof TypeError) {
      console.log('Type problem:', err.message);
    } else if (err instanceof Error) {
      console.log('Generic error:', err.message);
    }
  }
}

Returning errors instead of throwing:

async function tryFetch(url) {
  try {
    const res = await fetch(url);
    return { ok: true, data: await res.json() };
  } catch (err) {
    return { ok: false, error: err.message };
  }
}

const result = await tryFetch('/api');
if (result.ok) {
  console.log(result.data);
} else {
  console.log('Error:', result.error);
}

This pattern avoids try/catch at every call site.

Re-throwing errors:

async function load() {
  try {
    return await fetchData();
  } catch (err) {
    console.error('Debug: failed', err);
    throw err;   // let the caller decide
  }
}

await on a rejected Promise without catch:

async function run() {
  await Promise.reject(new Error('unhandled'));
}

run();   // ❌ unhandled rejection

Always wrap in try/catch or attach .catch() at the call site.

Combining async/await with .catch():

const data = await fetchData().catch(err => {
  console.error(err);
  return null;
});

You can mix .catch() on individual Promises with try/catch for the whole function.

Error handling in parallel:

async function loadAll() {
  try {
    const results = await Promise.all([a(), b(), c()]);
    return results;
  } catch (err) {
    console.log('One failed:', err.message);
    return null;
  }
}

Promise.all rejects with the first failure — one try/catch handles it.

Handling partial failures:

async function loadPartial() {
  const results = await Promise.allSettled([a(), b(), c()]);
  const ok = results.filter(r => r.status === 'fulfilled').map(r => r.value);
  const failed = results.filter(r => r.status === 'rejected').map(r => r.reason);
  return { ok, failed };
}

Use allSettled when you want to know what succeeded and what failed.

Retry logic with async/await:

async function retry(fn, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try {
      return await fn();
    } catch (err) {
      if (i === attempts - 1) throw err;
      console.log(`Attempt ${i + 1} failed, retrying...`);
    }
  }
}

await retry(() => fetch('/flaky'));

Timeout pattern with async/await:

function withTimeout(promise, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), ms)
  );
  return Promise.race([promise, timeout]);
}

try {
  const data = await withTimeout(fetch('/slow'), 5000);
} catch (err) {
  console.log(err.message);
}
// [ Timeout ]

The key insight — async/await + try/catch = sync-style error handling:

// Sync
try {
  const data = parseJson(text);
} catch (err) {
  console.error(err);
}

// Async
try {
  const data = await fetchJson(url);
} catch (err) {
  console.error(err);
}

Same shape, same mental model. That’s the payoff of async/await.

Unhandled rejections — the trap:

// ❌ Floating Promise — no catch
async function run() {
  fetchData();   // forgot await and .catch
}

// ✅ Handled
async function run() {
  try {
    await fetchData();
  } catch (err) {
    console.error(err);
  }
}

// ✅ Or with .catch()
async function run() {
  fetchData().catch(err => console.error(err));
}

Every Promise chain must end in .catch() or be inside a try/catch with await.


Complete Example Session

// ============================================
// PART 1: BASIC ASYNC
// ============================================

async function greet() {
  return 'Hello';
}

greet().then(v => console.log(v));
// [ Hello ]

// ============================================
// PART 2: BASIC AWAIT
// ============================================

async function run() {
  const value = await Promise.resolve(42);
  console.log(value);
}

run();
// [ 42 ]

// ============================================
// PART 3: AWAIT ON NON-PROMISE
// ============================================

async function run() {
  const a = await 42;
  const b = await 'hi';
  console.log(a, b);
}

run();
// [ 42 hi ]

// ============================================
// PART 4: ASYNC RETURNS PROMISE
// ============================================

async function getValue() {
  return 42;
}

console.log(getValue());
// [ Promise { 42 } ]

getValue().then(v => console.log(v));
// [ 42 ]

// ============================================
// PART 5: THROW IN ASYNC
// ============================================

async function fails() {
  throw new Error('boom');
}

fails().catch(err => console.log(err.message));
// [ boom ]

// ============================================
// PART 6: TRY/CATCH
// ============================================

async function run() {
  try {
    const res = await Promise.reject(new Error('network'));
  } catch (err) {
    console.log('Caught:', err.message);
  }
}

run();
// [ Caught: network ]

// ============================================
// PART 7: TRY/CATCH/FINALLY
// ============================================

async function run() {
  try {
    await Promise.resolve('ok');
    console.log('Try');
  } catch (err) {
    console.log('Catch');
  } finally {
    console.log('Finally');
  }
}

run();
// [ Try ]
// [ Finally ]

// ============================================
// PART 8: SEQUENTIAL
// ============================================

async function sequential() {
  const a = await Promise.resolve(1);
  const b = await Promise.resolve(a + 1);
  const c = await Promise.resolve(b + 1);
  return c;
}

sequential().then(v => console.log(v));
// [ 3 ]

// ============================================
// PART 9: PARALLEL WITH PROMISE.ALL
// ============================================

async function parallel() {
  const [a, b, c] = await Promise.all([
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3)
  ]);
  return [a, b, c];
}

parallel().then(v => console.log(v));
// [ [ 1, 2, 3 ] ]

// ============================================
// PART 10: AWAIT IN A LOOP
// ============================================

async function loop() {
  const results = [];
  for (const x of [1, 2, 3]) {
    results.push(await Promise.resolve(x * 10));
  }
  return results;
}

loop().then(v => console.log(v));
// [ [ 10, 20, 30 ] ]

// ============================================
// PART 11: PARALLEL IN A LOOP
// ============================================

async function parallelLoop() {
  return Promise.all([1, 2, 3].map(x => Promise.resolve(x * 10)));
}

parallelLoop().then(v => console.log(v));
// [ [ 10, 20, 30 ] ]

// ============================================
// PART 12: FOREACH DOESN'T AWAIT
// ============================================

async function badLoop() {
  [1, 2, 3].forEach(async x => {
    await Promise.resolve(x);
    console.log('Processed', x);
  });
  console.log('Done');
}

badLoop();
// [ Done ]
// [ Processed 1 ]
// [ Processed 2 ]
// [ Processed 3 ]
// ❌ Order is wrong

// ============================================
// PART 13: FOR...OF DOES AWAIT
// ============================================

async function goodLoop() {
  for (const x of [1, 2, 3]) {
    await Promise.resolve(x);
    console.log('Processed', x);
  }
  console.log('Done');
}

goodLoop();
// [ Processed 1 ]
// [ Processed 2 ]
// [ Processed 3 ]
// [ Done ]
// ✅ Order is correct

// ============================================
// PART 14: ASYNC ARROW FUNCTION
// ============================================

const fetchData = async () => {
  return await Promise.resolve('data');
};

fetchData().then(v => console.log(v));
// [ data ]

// ============================================
// PART 15: ASYNC METHOD
// ============================================

const api = {
  async getUser(id) {
    return { id, name: 'Alice' };
  }
};

api.getUser(1).then(u => console.log(u));
// [ { id: 1, name: 'Alice' } ]

// ============================================
// PART 16: ASYNC CLASS METHOD
// ============================================

class Service {
  async fetchUser(id) {
    return await Promise.resolve({ id, name: 'Alice' });
  }
}

new Service().fetchUser(1).then(u => console.log(u.name));
// [ Alice ]

// ============================================
// PART 17: IIFE
// ============================================

(async () => {
  const data = await Promise.resolve('from IIFE');
  console.log(data);
})();
// [ from IIFE ]

// ============================================
// PART 18: RETRY PATTERN
// ============================================

async function retry(fn, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try {
      return await fn();
    } catch (err) {
      if (i === attempts - 1) throw err;
      console.log(`Attempt ${i + 1} failed`);
    }
  }
}

let count = 0;
retry(async () => {
  count++;
  if (count < 3) throw new Error('fail');
  return 'success';
}).then(v => console.log(v));
// [ Attempt 1 failed ]
// [ Attempt 2 failed ]
// [ success ]

// ============================================
// PART 19: TIMEOUT
// ============================================

function withTimeout(promise, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), ms)
  );
  return Promise.race([promise, timeout]);
}

async function run() {
  try {
    await withTimeout(
      new Promise(r => setTimeout(() => r('slow'), 2000)),
      100
    );
  } catch (err) {
    console.log(err.message);
  }
}

run();
// [ Timeout ]

// ============================================
// PART 20: FULL SCRIPT
// ============================================

async function fetchData43() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();
  return data;
}

fetchData43().then(data => console.log(data));

async function run43() {
  try {
    const result = await fetchData43();
    console.log(result);
  } catch (err) {
    console.error('Failed:', err.message);
  } finally {
    console.log('Done');
  }
}

run43();

async function sequential43() {
  const a = await Promise.resolve(1);
  const b = await Promise.resolve(a + 1);
  return Promise.resolve(b + 1);
}

async function parallel43() {
  const [a, b] = await Promise.all([
    Promise.resolve(1),
    Promise.resolve(2)
  ]);
  return [a, b];
}

sequential43().then(v => console.log(v));
parallel43().then(v => console.log(v));

Quick Reference

async/await Basics

SyntaxMeaning
async function fn() {}Returns a Promise
await promisePauses until settled
async () => {}Async arrow function
async method() {}Async method
(async () => {})()Async IIFE

async Rules

RuleDetails
Always returns a PromiseEven for plain returns
Throws become rejectionsInside the async function
Can return PromisesThey’re unwrapped
await only inside asyncElse SyntaxError

await Rules

RuleDetails
Works on any valueNon-Promises resolve instantly
Throws on rejectionUse try/catch
Pauses only the functionOther code keeps running
Low precedenceBreak lines when chaining

Sequential vs Parallel

PatternCode
Sequentialconst a = await f(); const b = await g();
Parallelconst [a, b] = await Promise.all([f(), g()]);
Loop (ordered)for (const x of arr) await f(x);
Loop (parallel)await Promise.all(arr.map(x => f(x)));

Error Handling

PatternCode
try/catchtry { await f(); } catch (e) {}
try/catch/finallytry {} catch {} finally {}
Return errorreturn { ok: false, error }
Re-throwcatch (e) { throw e; }
.catch() on Promiseawait f().catch(...)
allSettledconst r = await Promise.allSettled(arr)

Common Async Patterns

PatternDescription
SequentialEach step depends on previous
ParallelIndependent steps
TimeoutPromise.race with timer
RetryLoop with try/catch
FallbackCatch and provide default
BatchingPromise.all in chunks
Concurrency limitBatch + parallel

async vs .then()

Aspectasync/await.then()
ReadabilitySync-likeChain-based
Error handlingtry/catch.catch()
DebuggingBetter stackCompressed
LoopsNativeAwkward
ConditionalsNativeHarder
Top-levelES modulesWorks

Pitfalls

PitfallFix
Missing awaitAdd await
forEach with asyncUse for...of
Await in loopUse Promise.all
No try/catchAdd one
await outside asyncMove into function
Floating PromisesAlways .catch()

Best Practices

Do This:

// Always handle errors
async function run() {
  try { await f(); } catch (e) { log(e); }
}                                              // ✅

// Use Promise.all for parallel
const [a, b] = await Promise.all([f(), g()]);  // ✅

// Use for...of for sequential loops
for (const x of arr) await f(x);               // ✅

// Use async arrow functions for callbacks
items.map(async x => await f(x));              // ✅ with Promise.all

// Return values directly
async function f() {
  return await g();                            // ✅
}

// Use allSettled for partial failures
const r = await Promise.allSettled(arr);       // ✅

// Wrap floating Promises
void fetchData().catch(log);                   // ✅

// Use top-level await in modules
const data = await fetch('/api');              // ✅ in .mjs

Don’t Do This:

// Don't forget await
const data = fetch('/x');                      // ❌ Promise, not data
const data = await fetch('/x');                // ✅

// Don't use forEach with async
arr.forEach(async x => await f(x));            // ❌ doesn't wait
for (const x of arr) await f(x);               // ✅

// Don't await in a loop when parallel is fine
for (const u of urls) await fetch(u);          // ⚠️  sequential
await Promise.all(urls.map(fetch));            // ✅

// Don't swallow errors silently
try { await f(); } catch {}                    // ❌

// Don't await unnecessarily
const x = await 42;                            // ⚠️  works but odd
const x = 42;                                  // ✅

// Don't mix callbacks and Promises
fs.readFile('x', async (err, data) => ...);    // ❌

// Don't forget the async keyword
function f() { await g(); }                    // ❌ SyntaxError

// Don't rely on order in Promise.all
const [a, b] = await Promise.all([f(), g()]);  // ✅ order by array

Common Pitfalls

PitfallProblemSolution
Missing awaitPromise instead of valueAdd await
forEach with asyncLoop doesn’t waitfor...of
Sequential in loopSlowPromise.all + map
No try/catchUnhandled rejectionWrap in try
await outside asyncSyntaxErrorMove to async function
Floating PromiseSilent failure.catch() or await
Awaiting non-PromiseUnnecessarySkip await
Lost order in parallelRacePreserve array order

Real-World Examples

1. Basic async function

async function greet() {
  return 'Hello';
}

greet().then(v => console.log(v));
// [ Hello ]

2. Await a Promise

async function run() {
  const value = await Promise.resolve(42);
  console.log(value);
}

run();
// [ 42 ]

3. Await a non-Promise

async function run() {
  const a = await 42;
  console.log(a);
}

run();
// [ 42 ]

4. Async returns Promise

async function getValue() {
  return 42;
}

getValue().then(v => console.log(v));
// [ 42 ]

5. Throw in async

async function fails() {
  throw new Error('boom');
}

fails().catch(err => console.log(err.message));
// [ boom ]

6. try/catch

async function run() {
  try {
    await Promise.reject(new Error('network'));
  } catch (err) {
    console.log('Caught:', err.message);
  }
}

run();
// [ Caught: network ]

7. try/catch/finally

async function run() {
  try {
    await Promise.resolve('ok');
    console.log('Try');
  } catch (err) {
    console.log('Catch');
  } finally {
    console.log('Finally');
  }
}

run();
// [ Try ]
// [ Finally ]

8. Sequential

async function sequential() {
  const a = await Promise.resolve(1);
  const b = await Promise.resolve(a + 1);
  const c = await Promise.resolve(b + 1);
  return c;
}

sequential().then(v => console.log(v));
// [ 3 ]

9. Parallel

async function parallel() {
  const [a, b] = await Promise.all([
    Promise.resolve(1),
    Promise.resolve(2)
  ]);
  return [a, b];
}

parallel().then(v => console.log(v));
// [ [ 1, 2 ] ]

10. Sequential loop

async function loop() {
  const results = [];
  for (const x of [1, 2, 3]) {
    results.push(await Promise.resolve(x * 10));
  }
  return results;
}

loop().then(v => console.log(v));
// [ [ 10, 20, 30 ] ]

11. Parallel loop

async function parallelLoop() {
  return Promise.all([1, 2, 3].map(x => Promise.resolve(x * 10)));
}

parallelLoop().then(v => console.log(v));
// [ [ 10, 20, 30 ] ]

12. Async arrow

const fetchData = async () => {
  return await Promise.resolve('data');
};

fetchData().then(v => console.log(v));
// [ data ]

13. Async method

const api = {
  async getUser(id) {
    return { id, name: 'Alice' };
  }
};

api.getUser(1).then(u => console.log(u));
// [ { id: 1, name: 'Alice' } ]

14. Async class method

class Service {
  async fetchUser(id) {
    return await Promise.resolve({ id, name: 'Alice' });
  }
}

new Service().fetchUser(1).then(u => console.log(u.name));
// [ Alice ]

15. Async IIFE

(async () => {
  const data = await Promise.resolve('from IIFE');
  console.log(data);
})();
// [ from IIFE ]

16. Retry pattern

async function retry(fn, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try {
      return await fn();
    } catch (err) {
      if (i === attempts - 1) throw err;
    }
  }
}

17. Timeout pattern

function withTimeout(promise, ms) {
  const timeout = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Timeout')), ms)
  );
  return Promise.race([promise, timeout]);
}

async function run() {
  try {
    await withTimeout(new Promise(r => setTimeout(() => r('slow'), 2000)), 100);
  } catch (err) {
    console.log(err.message);
  }
}

run();
// [ Timeout ]

18. Sequential file processing

async function processFiles(files) {
  for (const file of files) {
    await processFile(file);
  }
}

19. Parallel data loading

async function loadDashboard(userId) {
  const [user, orders, messages] = await Promise.all([
    fetch(`/users/${userId}`).then(r => r.json()),
    fetch(`/users/${userId}/orders`).then(r => r.json()),
    fetch(`/users/${userId}/messages`).then(r => r.json())
  ]);
  return { user, orders, messages };
}

20. Full Script

async function fetchData43() {
  const res = await fetch('https://api.example.com/data');
  const data = await res.json();
  return data;
}

fetchData43().then(data => console.log(data));

async function run43() {
  try {
    const result = await fetchData43();
    console.log(result);
  } catch (err) {
    console.error('Failed:', err.message);
  } finally {
    console.log('Done');
  }
}

run43();

const p1 = fetchData43();
const p2 = fetchData43();
const [a, b] = await Promise.all([p1, p2]);

async function sequential43() {
  const a = await Promise.resolve(1);
  const b = await Promise.resolve(a + 1);
  return Promise.resolve(b + 1);
}

async function parallel43() {
  const [a, b] = await Promise.all([
    Promise.resolve(1),
    Promise.resolve(2)
  ]);
  return [a, b];
}

Visual: async/await Flow

┌──────────────────────────────────────────────┐
│  async function run() {                      │
│    console.log('start');                     │
│    const data = await fetchData();           │
│    console.log('got:', data);                │
│    return data;                              │
│  }                                           │
│                                              │
│  Caller:                                     │
│    run();                                    │
│    console.log('after call');                │
│                                              │
│  Order:                                      │
│    1. start                                  │
│    2. after call   ← caller continues        │
│    3. got: data    ← resumes after await     │
│                                              │
│  await pauses ONLY the async function,       │
│  not the caller.                             │
│                                              │
└──────────────────────────────────────────────┘

Visual: Sequential vs Parallel

┌──────────────────────────────────────────────┐
│           Sequential                         │
│                                              │
│  await a()  ──────►  a done                  │
│  await b()  ──────►  b done                  │
│  await c()  ──────►  c done                  │
│                                              │
│  Total: sum of times                         │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│           Parallel                           │
│                                              │
│  await Promise.all([a(), b(), c()])          │
│                                              │
│  a() ────────────►                           │
│  b() ────────────►                           │
│  c() ────────────►                           │
│       │                                      │
│       └──► all done                          │
│                                              │
│  Total: max of times                         │
│                                              │
└──────────────────────────────────────────────┘

Visual: Error Handling

┌──────────────────────────────────────────────┐
│  try {                                       │
│    const data = await fetchData();           │
│    // runs if resolved                       │
│  } catch (err) {                             │
│    // runs if rejected OR throws             │
│  } finally {                                 │
│    // always runs                            │
│  }                                           │
│                                              │
│  Same shape as synchronous try/catch.        │
│                                              │
└──────────────────────────────────────────────┘

Summary

ConceptSyntaxExample
Async functionasync function fn() {}async function run() {}
Async arrowasync () => {}const f = async () => {}
Async methodasync method() {}async fetch() {}
Async IIFE(async () => {})()Run at top level
Awaitawait promiseconst x = await f()
Await non-Promiseawait 42Resolves instantly
Returnreturn valueWrapped in Promise
Throwthrow errRejects the Promise
try/catchtry { await f() } catch (e) {}Handle errors
Sequentialawait a; await b;One at a time
Parallelawait Promise.all([a, b])All at once
Loop (ordered)for (const x of arr) await f(x)Sequential
Loop (parallel)await Promise.all(arr.map(f))Concurrent
RetryLoop with try/catchRe-attempt
TimeoutPromise.race with timerFail fast

Key takeaways:

  • async marks a function as returning a Promise — always
  • await pauses the async function until a Promise settles and returns its value
  • await throws on rejection — wrap in try/catch
  • await only pauses the current function — other code keeps running
  • Use try/catch/finally — same shape as sync code
  • Sequential: await one after another; parallel: await Promise.all
  • Don’t use forEach with async — it doesn’t wait; use for...of or map + Promise.all
  • Returning a Promise from async unwraps it — you get the resolved value
  • await on non-Promises resolves immediately — safe but usually unnecessary
  • Top-level await works in ES modules and modern Node
  • Always handle errors — unhandled rejections crash or warn
  • Master async/await and Promises become readable

Remember: async/await is Promises with better syntax. Mark a function async, write await in front of any Promise, handle errors with try/catch. Use Promise.all for parallel, for...of for sequential. Never forget await. Never use forEach with async. Always catch errors. Master async/await, and asynchronous JavaScript stops being scary — it just looks like regular code.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!