Module 6: Asynchronous JavaScript

Master asynchronous programming with promises, async/await, and generators.

Back to Course|2.5 hours|Intermediate

Asynchronous JavaScript

Master asynchronous programming with promises, async/await, and generators.

Progress: 0/3 topics completed0%

Select Topics Overview

JavaScript Promises

Learn to work with promises, handle asynchronous operations, and manage promise chains for better error handling and code organization

Content by: Nishant Darji

JavaScript Developer

Connect

Promise Fundamentals

Promises represent the eventual completion (or failure) of an asynchronous operation and its resulting value. They provide a cleaner alternative to callback-based asynchronous code and help avoid callback hell.

Creating Promises

Code Example
// Creating promises
// Basic promise
const basicPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Success!');
    }, 1000);
});

// Promise with error
const errorPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(new Error('Something went wrong!'));
    }, 1000);
});

// Promise with conditional logic
function fetchUserData(userId) {
    return new Promise((resolve, reject) => {
        if (!userId) {
            reject(new Error('User ID is required'));
            return;
        }
        
        // Simulate API call
        setTimeout(() => {
            const userData = {
                id: userId,
                name: 'John Doe',
                email: 'john@example.com'
            };
            resolve(userData);
        }, 1000);
    });
}

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

// Using promises
basicPromise
    .then(result => console.log(result))
    .catch(error => console.error(error));

errorPromise
    .then(result => console.log(result))
    .catch(error => console.error(error.message));

fetchUserData(123)
    .then(user => console.log('User:', user))
    .catch(error => console.error('Error:', error.message));

// Promise chaining
fetchUserData(123)
    .then(user => {
        console.log('User fetched:', user);
        return fetchUserPosts(user.id);
    })
    .then(posts => {
        console.log('Posts fetched:', posts);
        return fetchUserComments(user.id);
    })
    .then(comments => {
        console.log('Comments fetched:', comments);
    })
    .catch(error => {
        console.error('Error in chain:', error.message);
    });
Swipe to see more code

Promise Methods

Code Example
// Promise.all - Wait for all promises
const promises = [
    fetchUserData(1),
    fetchUserData(2),
    fetchUserData(3)
];

Promise.all(promises)
    .then(users => {
        console.log('All users:', users);
    })
    .catch(error => {
        console.error('One of the promises failed:', error);
    });

// Promise.allSettled - Wait for all promises to settle
Promise.allSettled(promises)
    .then(results => {
        results.forEach((result, index) => {
            if (result.status === 'fulfilled') {
                console.log(`Promise ${index} succeeded:`, result.value);
            } else {
                console.log(`Promise ${index} failed:`, result.reason);
            }
        });
    });

// Promise.race - First promise to settle
const fastPromise = new Promise(resolve => setTimeout(() => resolve('Fast'), 100));
const slowPromise = new Promise(resolve => setTimeout(() => resolve('Slow'), 1000));

Promise.race([fastPromise, slowPromise])
    .then(result => console.log('Winner:', result)); // "Fast"

// Promise.any - First promise to fulfill
const errorPromise = new Promise((_, reject) => setTimeout(() => reject('Error'), 500));
const successPromise = new Promise(resolve => setTimeout(() => resolve('Success'), 1000));

Promise.any([errorPromise, successPromise])
    .then(result => console.log('First success:', result)) // "Success"
    .catch(error => console.log('All promises failed:', error));

// Promise.resolve and Promise.reject
const resolvedPromise = Promise.resolve('Immediate success');
const rejectedPromise = Promise.reject(new Error('Immediate error'));

// Converting callback-based functions to promises
function promisify(fn) {
    return function(...args) {
        return new Promise((resolve, reject) => {
            fn(...args, (error, result) => {
                if (error) {
                    reject(error);
                } else {
                    resolve(result);
                }
            });
        });
    };
}

// Example usage
const fs = require('fs');
const readFileAsync = promisify(fs.readFile);

readFileAsync('file.txt', 'utf8')
    .then(content => console.log('File content:', content))
    .catch(error => console.error('File read error:', error));
Swipe to see more code

Practice Exercise: Promises

Code Example
// Exercise: Build a Promise-based API Client
class ApiClient {
    constructor(baseUrl) {
        this.baseUrl = baseUrl;
        this.cache = new Map();
    }
    
    request(endpoint, options = {}) {
        const url = `${this.baseUrl}${endpoint}`;
        const cacheKey = `${options.method || 'GET'}-${url}`;
        
        // Check cache for GET requests
        if (options.method === 'GET' && this.cache.has(cacheKey)) {
            return Promise.resolve(this.cache.get(cacheKey));
        }
        
        return fetch(url, {
            headers: {
                'Content-Type': 'application/json',
                ...options.headers
            },
            ...options
        })
        .then(response => {
            if (!response.ok) {
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            return response.json();
        })
        .then(data => {
            // Cache GET responses
            if (options.method === 'GET') {
                this.cache.set(cacheKey, data);
            }
            return data;
        });
    }
    
    get(endpoint) {
        return this.request(endpoint, { method: 'GET' });
    }
    
    post(endpoint, data) {
        return this.request(endpoint, {
            method: 'POST',
            body: JSON.stringify(data)
        });
    }
    
    put(endpoint, data) {
        return this.request(endpoint, {
            method: 'PUT',
            body: JSON.stringify(data)
        });
    }
    
    delete(endpoint) {
        return this.request(endpoint, { method: 'DELETE' });
    }
    
    clearCache() {
        this.cache.clear();
    }
}

// Exercise: Build a Promise Queue
class PromiseQueue {
    constructor() {
        this.queue = [];
        this.running = false;
    }
    
    add(task) {
        return new Promise((resolve, reject) => {
            this.queue.push({
                task,
                resolve,
                reject
            });
            
            if (!this.running) {
                this.process();
            }
        });
    }
    
    async process() {
        this.running = true;
        
        while (this.queue.length > 0) {
            const { task, resolve, reject } = this.queue.shift();
            
            try {
                const result = await task();
                resolve(result);
            } catch (error) {
                reject(error);
            }
        }
        
        this.running = false;
    }
    
    getLength() {
        return this.queue.length;
    }
    
    clear() {
        this.queue = [];
    }
}

// Exercise: Build a Retry Mechanism
function retry(fn, maxAttempts = 3, delay = 1000) {
    return new Promise((resolve, reject) => {
        let attempts = 0;
        
        function attempt() {
            attempts++;
            
            fn()
                .then(resolve)
                .catch(error => {
                    if (attempts >= maxAttempts) {
                        reject(error);
                    } else {
                        console.log(`Attempt ${attempts} failed, retrying in ${delay}ms...`);
                        setTimeout(attempt, delay);
                    }
                });
        }
        
        attempt();
    });
}

// Test the exercises
const api = new ApiClient('https://api.example.com');

// Test API client
api.get('/users')
    .then(users => console.log('Users:', users))
    .catch(error => console.error('API error:', error));

// Test promise queue
const queue = new PromiseQueue();

queue.add(() => new Promise(resolve => setTimeout(() => resolve('Task 1'), 1000)));
queue.add(() => new Promise(resolve => setTimeout(() => resolve('Task 2'), 500)));
queue.add(() => new Promise(resolve => setTimeout(() => resolve('Task 3'), 200)));

// Test retry mechanism
const unreliableFunction = () => {
    return new Promise((resolve, reject) => {
        if (Math.random() > 0.5) {
            resolve('Success!');
        } else {
            reject(new Error('Random failure'));
        }
    });
};

retry(unreliableFunction, 3, 1000)
    .then(result => console.log('Retry succeeded:', result))
    .catch(error => console.error('Retry failed:', error));
Swipe to see more code

🎯 Practice Exercise

Test your understanding of this topic:

Ready for the Next Module?

Continue your learning journey and master the next set of concepts.

Continue to Module 7