Skip to main content
โšก Calmops

JavaScript Async/Await Patterns Complete Guide 2026

Introduction

Asynchronous programming is fundamental to JavaScript. Understanding modern async patterns helps you write cleaner, more efficient code.

This guide covers async JavaScript in 2026.

Promises

Basic Promise

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Data loaded');
  }, 1000);
});

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

Promise Methods

// Promise.all - all must succeed
Promise.all([
  fetch('/api/users'),
  fetch('/api/posts'),
])
  .then(([users, posts]) => console.log(users, posts));

// Promise.race - first to complete
Promise.race([
  fetch('/api/fast'),
  new Promise((_, reject) => 
    setTimeout(() => reject(new Error('Timeout')), 5000)
])
]);

// Promise.allSettled - all complete
Promise.allSettled([
  fetch('/api/users'),
  fetch('/api/posts'),
]).then((results) => {
  results.forEach((result) => {
    if (result.status === 'fulfilled') {
      console.log(result.value);
    } else {
      console.error(result.reason);
    }
  });
});

// Promise.any - first success
Promise.any([
  fetch('/api/primary'),
  fetch('/api/backup'),
]).then((data) => console.log(data));

Async/Await

Basic Usage

async function fetchData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
}

Parallel Execution

// Sequential - slower
const user = await fetchUser(id);
const posts = await fetchPosts(id);

// Parallel - faster
const [user, posts] = await Promise.all([
  fetchUser(id),
  fetchPosts(id),
]);

Error Handling

// Try-catch
async function getData() {
  try {
    const data = await fetchData();
    return data;
  } catch (error) {
    console.error(error);
    return null;
  }
}

// Error boundaries
async function getData() {
  const data = await fetchData().catch((err) => {
    console.error(err);
    return defaultData;
  });
  return data;
}

Async Patterns

Retry Pattern

async function retry(fn, maxAttempts = 3, delay = 1000) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxAttempts) throw error;
      await new Promise((r) => setTimeout(r, delay * attempt));
    }
  }
}

// Usage
const data = await retry(() => fetch('/api/data'));

Timeout Pattern

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

// Usage
try {
  const data = await withTimeout(fetch('/api/data'), 5000);
} catch (error) {
  console.error('Request timed out');
}

Queue Pattern

class AsyncQueue {
  constructor(concurrency = 2) {
    this.concurrency = concurrency;
    this.running = 0;
    this.queue = [];
  }

  async add(fn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ fn, resolve, reject });
      this.process();
    });
  }

  async process() {
    while (this.running < this.concurrency && this.queue.length) {
      const { fn, resolve, reject } = this.queue.shift();
      this.running++;
      
      try {
        const result = await fn();
        resolve(result);
      } catch (error) {
        reject(error);
      } finally {
        this.running--;
        this.process();
      }
    }
  }
}

// Usage
const queue = new AsyncQueue(3);
const results = await Promise.all(
  items.map(item => queue.add(() => processItem(item)))

Debounce & Throttle

// Debounce - wait for pause
function debounce(fn, delay) {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn(...args), delay);
  };
}

// Throttle - limit frequency
function throttle(fn, limit) {
  let inThrottle;
  return (...args) => {
    if (!inThrottle) {
      fn(...args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
}

// Usage
const debouncedSearch = debounce((query) => search(query), 300);
const throttledScroll = throttle(() => handleScroll(), 100);

Modern Async Features

For Await Of

async function* fetchPages() {
  let page = 1;
  while (true) {
    const data = await fetch(`/api/items?page=${page}`);
    const items = await data.json();
    if (items.length === 0) break;
    yield items;
    page++;
  }
}

// Usage
for await (const items of fetchPages()) {
  console.log(items);
}

Promise.withResolvers

// Modern alternative to new Promise
const { promise, resolve, reject } = Promise.withResolvers();

fetch('/api/data')
  .then(resolve)
  .catch(reject);

const data = await promise;

Conclusion

Master async patterns for better JavaScript. Use Promise.all for parallel operations, implement retry for reliability, and handle errors gracefully.

Resources

Comments