Bab 5: Asynchronous Programming

Bab 5: Asynchronous Programming

5.1 Pengenalan Asynchronous Programming

5.1.1 Konsep Dasar

  • Perbedaan Synchronous vs Asynchronous

  • Single-threaded nature of JavaScript

  • Call Stack dan Event Loop

  • Blocking vs Non-blocking operations

  • Use cases untuk asynchronous programming

5.1.2 Event Loop Explained

console.log('Start');

setTimeout(() => {
    console.log('Timeout 1');
}, 0);

Promise.resolve().then(() => {
    console.log('Promise 1');
});

console.log('End');

// Output:
// Start
// End
// Promise 1
// Timeout 1

5.2 Callback

5.2.1 Basic Callbacks

// Simple callback example
function fetchData(callback) {
    setTimeout(() => {
        const data = { id: 1, name: 'John' };
        callback(data);
    }, 1000);
}

fetchData((result) => {
    console.log(result);
});

// Error handling with callbacks
function fetchDataWithError(success, error) {
    setTimeout(() => {
        const random = Math.random();
        if (random > 0.5) {
            success({ id: 1, name: 'John' });
        } else {
            error(new Error('Failed to fetch data'));
        }
    }, 1000);
}

5.2.2 Callback Hell

// Example of callback hell
fetchUser(function(user) {
    fetchUserPosts(user.id, function(posts) {
        fetchPostComments(posts[0].id, function(comments) {
            fetchCommentAuthor(comments[0].id, function(author) {
                console.log(author);
            }, function(error) {
                console.error('Error:', error);
            });
        }, function(error) {
            console.error('Error:', error);
        });
    }, function(error) {
        console.error('Error:', error);
    });
}, function(error) {
    console.error('Error:', error);
});

// Solution using named functions
function handleAuthor(author) {
    console.log(author);
}

function handleComments(comments) {
    fetchCommentAuthor(comments[0].id, handleAuthor, handleError);
}

function handlePosts(posts) {
    fetchPostComments(posts[0].id, handleComments, handleError);
}

function handleUser(user) {
    fetchUserPosts(user.id, handlePosts, handleError);
}

function handleError(error) {
    console.error('Error:', error);
}

fetchUser(handleUser, handleError);

5.3 Promise

5.3.1 Promise Basics

// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
    // Async operation
    setTimeout(() => {
        const random = Math.random();
        if (random > 0.5) {
            resolve('Success!');
        } else {
            reject(new Error('Failed!'));
        }
    }, 1000);
});

// Using a Promise
myPromise
    .then(result => console.log(result))
    .catch(error => console.error(error))
    .finally(() => console.log('Completed'));

// Chaining Promises
fetchUser()
    .then(user => fetchUserPosts(user.id))
    .then(posts => fetchPostComments(posts[0].id))
    .then(comments => fetchCommentAuthor(comments[0].id))
    .then(author => console.log(author))
    .catch(error => console.error('Error:', error));

5.3.2 Promise Methods

// Promise.all
const promises = [
    fetch('/api/users'),
    fetch('/api/posts'),
    fetch('/api/comments')
];

Promise.all(promises)
    .then(([users, posts, comments]) => {
        // All promises resolved
    })
    .catch(error => {
        // Any promise rejected
    });

// Promise.race
Promise.race([
    fetch('/api/fast'),
    fetch('/api/slow')
])
.then(result => {
    // First resolved promise
});

// Promise.allSettled
Promise.allSettled([
    Promise.resolve(1),
    Promise.reject('error'),
    Promise.resolve(3)
])
.then(results => {
    // All promises settled (resolved or rejected)
});

// Promise.any
Promise.any([
    Promise.reject('error'),
    Promise.resolve(1),
    Promise.resolve(2)
])
.then(result => {
    // First fulfilled promise
});

5.4 Async/Await

5.4.1 Basic Syntax

async function fetchUserData() {
    try {
        const user = await fetchUser();
        const posts = await fetchUserPosts(user.id);
        const comments = await fetchPostComments(posts[0].id);
        const author = await fetchCommentAuthor(comments[0].id);
        return author;
    } catch (error) {
        console.error('Error:', error);
    }
}

// Using async/await with Promise methods
async function fetchMultipleData() {
    try {
        const [users, posts, comments] = await Promise.all([
            fetch('/api/users'),
            fetch('/api/posts'),
            fetch('/api/comments')
        ]);
        return { users, posts, comments };
    } catch (error) {
        console.error('Error:', error);
    }
}

5.4.2 Error Handling

// Try-catch block
async function handleErrors() {
    try {
        const result = await riskyOperation();
        return result;
    } catch (error) {
        console.error('Error:', error);
        throw new Error('Failed to handle operation');
    } finally {
        console.log('Operation completed');
    }
}

// Multiple try-catch blocks
async function complexOperation() {
    try {
        const data = await fetchData();
        try {
            const processed = await processData(data);
            return processed;
        } catch (processingError) {
            console.error('Processing error:', processingError);
            return data; // Return raw data if processing fails
        }
    } catch (fetchError) {
        console.error('Fetch error:', fetchError);
        return null;
    }
}

5.5 Event Loop dan SetTimeout/SetInterval

5.5.1 SetTimeout

// Basic setTimeout
setTimeout(() => {
    console.log('Delayed message');
}, 1000);

// Clearing timeout
const timeoutId = setTimeout(() => {
    console.log('This will not run');
}, 1000);

clearTimeout(timeoutId);

// Nested timeouts
function nestedTimeout() {
    setTimeout(() => {
        console.log('First');
        setTimeout(() => {
            console.log('Second');
        }, 500);
    }, 1000);
}

5.5.2 SetInterval

// Basic setInterval
const intervalId = setInterval(() => {
    console.log('Repeating message');
}, 1000);

// Clearing interval
setTimeout(() => {
    clearInterval(intervalId);
}, 5000);

// Dynamic interval
let counter = 0;
const dynamicInterval = setInterval(() => {
    console.log(++counter);
    if (counter >= 5) {
        clearInterval(dynamicInterval);
    }
}, 1000);

5.6 Error Handling

5.6.1 Global Error Handling

// Unhandled Promise Rejection
window.addEventListener('unhandledrejection', event => {
    console.error('Unhandled promise rejection:', event.reason);
});

// Global error handler
window.onerror = function(message, source, lineno, colno, error) {
    console.error('Global error:', {
        message,
        source,
        lineno,
        colno,
        error
    });
    return true; // Prevents default error handling
};

5.7 Praktik dan Latihan

5.7.1 Project: Data Fetching Service

class DataService {
    async fetchData(url, options = {}) {
        try {
            const response = await fetch(url, options);
            if (!response.ok) {
                throw new Error(`HTTP error! status: ${response.status}`);
            }
            return await response.json();
        } catch (error) {
            console.error('Fetch error:', error);
            throw error;
        }
    }

    async fetchMultiple(urls) {
        try {
            const promises = urls.map(url => this.fetchData(url));
            return await Promise.all(promises);
        } catch (error) {
            console.error('Multiple fetch error:', error);
            throw error;
        }
    }
}

5.8 Best Practices

  • Proper error handling

  • Avoiding callback hell

  • Using async/await when possible

  • Managing multiple async operations

  • Performance considerations

5.9 Ringkasan

  • Asynchronous programming fundamentals

  • Different approaches to async operations

  • Error handling strategies

  • Common patterns dan solutions

5.10 Latihan Akhir Bab

  1. Create a data fetching library

  2. Implement a polling system

  3. Build an async task queue

  4. Create a promise-based cache system

  5. Implement retry mechanism for failed requests

Last updated