JavaScript Core/
Lesson

PromiseWhat is promise?An object that represents a value you don't have yet but will get in the future, letting your code keep running while it waits. chains solved callbackWhat is callback?A function you pass into another function to be called later, often when an operation finishes or an event occurs. hell, but reading .then() after .then() still forces you to jump around mentally to follow the flow. Async/awaitWhat is async/await?A syntax that lets you write asynchronous code (like fetching data) in a readable, step-by-step style instead of chaining callbacks. makes asynchronous code look and feel like regular synchronous code while keeping all the non-blocking benefits. It is syntactic sugar over Promises, nothing new under the hood, just dramatically better readability.

The async keyword

Adding async before a function transforms it in two ways: the function always returns a PromiseWhat is promise?An object that represents a value you don't have yet but will get in the future, letting your code keep running while it waits., and you can use await inside it.

// Regular function
function regular() {
  return 'Hello';
}
regular(); // 'Hello'

// Async function - automatically wraps return value in a Promise
async function asyncFunc() {
  return 'Hello';
}
asyncFunc(); // Promise { 'Hello' }
AI pitfall
AI tools frequently forget await when calling async functions, creating unhandled promise rejections. If you see const data = fetchUser(id) without await, data is a Promise object, not the actual user. The code runs without errors until you try to use data.name, then it fails silently or throws a confusing TypeError. Always check that every async call has await in front of it.
02

The await keyword

await pauses execution of the async function until a PromiseWhat is promise?An object that represents a value you don't have yet but will get in the future, letting your code keep running while it waits. settles, then returns the resolved value.

async function fetchUserData() {
  console.log('Starting fetch...');
  const response = await fetch('/api/user');   // Pauses here
  const user = await response.json();          // Pauses here
  console.log('User received:', user.name);
  return user;
}

// Other code runs while the function is paused
console.log('This runs immediately!');
fetchUserData();
console.log('This too!');
03

Converting promiseWhat is promise?An object that represents a value you don't have yet but will get in the future, letting your code keep running while it waits. chains to async/awaitWhat is async/await?A syntax that lets you write asynchronous code (like fetching data) in a readable, step-by-step style instead of chaining callbacks.

With Promises:

function loadProfile(userId) {
  return fetchUser(userId)
    .then(user => fetchUserPosts(user.id))
    .then(posts => ({ user, posts }))
    .catch(error => console.error('Failed:', error));
}

With async/await:

async function loadProfile(userId) {
  try {
    const user = await fetchUser(userId);
    const posts = await fetchUserPosts(user.id);
    return { user, posts };
  } catch (error) {
    console.error('Failed:', error);
    throw error;
  }
}

The async/await version reads like sequential steps: do this, then that, then return the result.

04

Sequential vs parallel execution

By default, each await pauses until that operation finishes before starting the next one. If the operations are independent, you waste time waiting.

PatternWhen to useTotal time
Sequential awaitEach step depends on the previousSum of all durations
Promise.all()Steps are independentDuration of the slowest
MixedSome dependent, some independentDepends on structure
// Sequential - each waits for the previous (slow)
async function sequential() {
  const user = await fetchUser(1);       // 300ms
  const posts = await fetchPosts(1);     // 300ms
  const comments = await fetchComments(1); // 300ms
  return { user, posts, comments };
} // Total: ~900ms

// Parallel - all start immediately (fast)
async function parallel() {
  const [user, posts, comments] = await Promise.all([
    fetchUser(1),
    fetchPosts(1),
    fetchComments(1)
  ]);
  return { user, posts, comments };
} // Total: ~300ms
05

Error handling with try/catch

async function getUserWithFallback(userId) {
  try {
    const user = await fetchUser(userId);
    return user;
  } catch (error) {
    console.warn('Primary failed:', error.message);
    try {
      return await fetchUserFromBackup(userId);
    } catch (backupError) {
      console.error('Backup also failed:', backupError.message);
      return null;
    }
  }
}

The finally block

Use finally for cleanup that must run regardless of success or failure:

async function loadWithSpinner() {
  showLoadingSpinner();
  try {
    const data = await fetchData();
    displayData(data);
  } catch (error) {
    showErrorMessage(error);
  } finally {
    hideLoadingSpinner(); // Always runs
  }
}
06

Common patterns

Processing items sequentially with per-item error handling:

async function processItems(items) {
  const results = [];
  for (const item of items) {
    try {
      const result = await processItem(item);
      results.push({ success: true, data: result });
    } catch (error) {
      results.push({ success: false, error: error.message });
    }
  }
  return results;
}

Important
You cannot use await inside Array.forEach(), it does not wait for each iteration. Use for...of for sequential async work, or Promise.all() with .map() for parallel.
07

Where you can use await

ContextWorks?Example
Inside async functionYesasync function f() { await ... }
Top-level in ES modulesYesconst x = await fetch(...)
Regular functionNoSyntaxError
Non-async arrow functionNoSyntaxError
08

Quick reference

PatternSyntax
Async functionasync function name() { ... }
Async arrowconst fn = async () => { ... }
Await a promiseconst result = await somePromise()
Error handlingtry { await ... } catch (e) { ... }
Parallel executionawait Promise.all([p1, p2, p3])
Cleanuptry { ... } finally { cleanup() }
javascript
async function getUser() {
  try {
    const response = await fetch('/api/user');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Failed:', error);
  }
}