Lập trình bất đồng bộ (asynchronous programming) trong JavaScript là một phương pháp lập trình cho phép một tác vụ bắt đầu mà không cần chờ hoàn thành để tiếp tục thực hiện các tác vụ khác. Trong lập trình đồng bộ, các lệnh được thực thi tuần tự; ngược lại, trong lập trình bất đồng bộ, các tác vụ có thể được thực thi song song hoặc đồng thời, giúp tăng hiệu quả và tránh việc làm chậm quá trình xử lý của chương trình.
JavaScript là ngôn ngữ đơn luồng, tức là chỉ có một luồng thực thi chính. Khi một tác vụ bất đồng bộ được thực hiện, JavaScript sử dụng cơ chế Event Loop để xử lý các tác vụ này, cho phép nó tiếp tục thực thi mã khác trong khi chờ các tác vụ dài hạn như truy vấn mạng, truy xuất file, hoặc bộ đếm thời gian hoàn tất.
Callback là một hàm được truyền như một tham số vào một hàm khác và được gọi sau khi tác vụ đã hoàn thành.
Ví dụ với Callback:
function doSomethingAsync(callback) { setTimeout(() => { console.log('Task done'); callback(); }, 2000); } doSomethingAsync(() => { console.log('Callback executed'); });
Trong ví dụ trên, sau 2 giây, chương trình thực hiện console.log('Task done')
và gọi hàm callback để tiếp tục.
Vấn đề với Callback: Callback có thể dẫn đến cái gọi là “Callback Hell”, nơi các callback lồng nhau dẫn đến mã trở nên khó đọc và bảo trì.
Promise cung cấp một cách tiếp cận sạch sẽ hơn để xử lý các tác vụ bất đồng bộ. Promise đại diện cho một giá trị chưa biết tại thời điểm nó được tạo ra nhưng sẽ được giải quyết (resolve) hoặc từ chối (reject) trong tương lai.
Ví dụ với Promise:
const doSomethingAsync = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('Task done'); }, 2000); }); }; doSomethingAsync() .then((message) => { console.log(message); // 'Task done' }) .catch((error) => { console.error(error); });
Promise giúp tránh lồng callback và cho phép chuỗi các tác vụ bất đồng bộ được thực hiện theo cách dễ hiểu và có thể quản lý được.
async
và await
là cú pháp được giới thiệu trong ES2017, cung cấp cách thức viết code bất đồng bộ trông giống như mã đồng bộ, giúp nó dễ đọc hơn và tránh được các vấn đề về callback hell hoặc quản lý Promise.
Ví dụ với Async/Await:
const doSomethingAsync = () => { return new Promise((resolve, reject) => { setTimeout(() => { resolve('Task done'); }, 2000); }); }; async function doTask() { try { const result = await doSomethingAsync(); console.log(result); // 'Task done' } catch (error) { console.error(error); } } doTask();
Trong ví dụ trên, từ khóa await
đợi cho Promise được giải quyết trước khi tiếp tục với mã tiếp theo, giúp mã trông rõ ràng và dễ hiểu hơn mà không cần xử lý .then()
.
Lập trình bất đồng bộ trong JavaScript rất quan trọng trong việc xử lý các tác vụ dài hạn mà không làm tắc nghẽn luồng chính. Các công cụ như callback, promise, và async/await cung cấp những cách khác nhau để xử lý bất đồng bộ, với async/await
là phương pháp hiện đại, dễ đọc và bảo trì nhất.