Back to BlogsProgramming

JavaScript Promises, Async/Await — Complete Guide with Examples

Anuj Singh (Admin) 30 April 2026 1187 views

Asynchronous JavaScript — The Complete Guide

JavaScript is single-threaded, but it handles asynchronous operations (API calls, file reads, timers) through its event loop. Understanding async JavaScript is essential for full-stack development and building security tools.

The Problem — Callback Hell

// Old way: callbacks (hard to read, error-prone)
getUserData(userId, function(user) {
    getOrders(user.id, function(orders) {
        getOrderDetails(orders[0].id, function(details) {
            calculateTotal(details, function(total) {
                // We're 4 levels deep — callback hell!
                displayTotal(total);
            });
        });
    });
});

Promises — The Modern Way

// A Promise represents a future value
const myPromise = new Promise((resolve, reject) => {
    const success = true;
    
    if (success) {
        resolve("Data loaded successfully");
    } else {
        reject(new Error("Something went wrong"));
    }
});

// Consume a promise with .then() and .catch()
myPromise
    .then(data => console.log(data))
    .catch(error => console.error(error))
    .finally(() => console.log("Always runs"));

// Promise chaining — flat and readable
getUserData(userId)
    .then(user => getOrders(user.id))
    .then(orders => getOrderDetails(orders[0].id))
    .then(details => calculateTotal(details))
    .then(total => displayTotal(total))
    .catch(error => handleError(error));

Async/Await — Clearest Syntax

// async functions always return a Promise
async function loadUserData(userId) {
    try {
        const user = await getUserData(userId);
        const orders = await getOrders(user.id);
        const details = await getOrderDetails(orders[0].id);
        const total = await calculateTotal(details);
        return total;
    } catch (error) {
        console.error('Error:', error.message);
        throw error;  // Re-throw if you want callers to handle it
    }
}

// Call an async function
loadUserData(123)
    .then(total => console.log('Total:', total))
    .catch(err => console.error('Failed:', err));

Fetch API — Making HTTP Requests

// GET request
async function fetchData() {
    const response = await fetch('https://api.example.com/users');
    
    if (!response.ok) {
        throw new Error('HTTP error: ' + response.status);
    }
    
    const data = await response.json();
    return data;
}

// POST request with JSON body
async function createUser(userData) {
    const response = await fetch('https://api.example.com/users', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer ' + localStorage.getItem('token')
        },
        body: JSON.stringify(userData)
    });
    
    const result = await response.json();
    return result;
}

// PUT request
async function updateUser(id, data) {
    const res = await fetch('/api/users/' + id, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data)
    });
    return res.json();
}

// DELETE request
async function deleteUser(id) {
    const res = await fetch('/api/users/' + id, { method: 'DELETE' });
    return res.json();
}

Parallel Requests — Promise.all

// Sequential (slow - waits for each):
const user = await fetchUser(id);
const posts = await fetchPosts(id);
// Total time: user_time + posts_time

// Parallel (fast - runs simultaneously):
const [user, posts] = await Promise.all([
    fetchUser(id),
    fetchPosts(id)
]);
// Total time: max(user_time, posts_time)

// Promise.allSettled — doesn't fail if one rejects:
const results = await Promise.allSettled([
    fetchFromServer1(),
    fetchFromServer2(),
    fetchFromServer3()
]);
results.forEach(result => {
    if (result.status === 'fulfilled') {
        console.log('Success:', result.value);
    } else {
        console.log('Failed:', result.reason);
    }
});

Error Handling Best Practices

// Centralized error handler
async function apiCall(url, options = {}) {
    try {
        const response = await fetch(url, options);
        
        if (!response.ok) {
            const errorData = await response.json().catch(() => ({}));
            throw new Error(errorData.message || 'Request failed');
        }
        
        return await response.json();
        
    } catch (error) {
        if (error.name === 'TypeError') {
            throw new Error('Network error — check your connection');
        }
        throw error;
    }
}

// Retry logic for flaky networks
async function fetchWithRetry(url, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            return await fetch(url).then(r => r.json());
        } catch (error) {
            if (i === maxRetries - 1) throw error;
            await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
        }
    }
}

🔥 Master JavaScript with our hands-on course →

Want to Learn This Practically?

Subscribe to ONLY4YOU and get hands-on access to 40+ premium courses — Ethical Hacking, Kali Linux, Metasploit, Network Hacking, Bug Bounty & more!