"Trước khi có Promise và Async/Await, Callback chính là cách Node.js xử lý các tác vụ bất đồng bộ. Hiểu Callback sẽ giúp bạn hiểu vì sao JavaScript hiện đại được thiết kế như ngày nay."
🎯 Mục tiêu bài học
Sau bài này bạn sẽ:
Hiểu Callback là gì.
Phân biệt Callback đồng bộ và bất đồng bộ.
Biết cách truyền Callback vào hàm.
Hiểu Callback Hell là gì.
Biết khi nào nên dùng Callback.
Hiểu vì sao Promise và Async/Await ra đời.
Callback là gì?
Callback là một hàm được truyền vào làm tham số của một hàm khác để được gọi lại (call back) khi cần.
Ví dụ đơn giản:
function hello(callback) {
console.log("Xin chào");
callback();
}
hello(() => {
console.log("Node.js");
});
Kết quả
Xin chào
Node.js
Ở đây:
hello()là hàm chính.Hàm mũi tên được truyền vào chính là Callback.
Vì sao cần Callback?
Giả sử bạn cần:
Đọc file.
Sau khi đọc xong mới xử lý dữ liệu.
Không thể:
readFile();
xuLy();
vì chưa chắc file đã đọc xong.
Thay vào đó:
readFile(() => {
xuLy();
});
Chỉ khi hoàn thành, Callback mới được gọi.
Callback đồng bộ
Ví dụ
function tinhTong(a, b, callback) {
const tong = a + b;
callback(tong);
}
tinhTong(5, 8, (kq) => {
console.log(kq);
});
↓
13
Ở đây Callback được gọi ngay lập tức, nên đây là Callback đồng bộ (Synchronous Callback).
Callback bất đồng bộ
Ví dụ
setTimeout(() => {
console.log("Đã xong");
}, 2000);
console.log("Đang chờ...");
↓
Đang chờ...
(sau 2 giây)
Đã xong
Callback chỉ chạy sau khi hết thời gian chờ.
Callback với fs
Ví dụ
import fs from "node:fs";
fs.readFile(
"data.txt",
"utf8",
(err, data) => {
if (err) {
console.log(err);
return;
}
console.log(data);
}
);
↓
Xin chào Node.js
Đây là kiểu Callback rất phổ biến trong Node.js.
Error-first Callback
Phần lớn API của Node.js sử dụng quy ước:
(err, data)
Ví dụ
fs.readFile(
"abc.txt",
"utf8",
(err, data) => {
if (err) {
console.log(err);
return;
}
console.log(data);
}
);
Nếu lỗi:
ENOENT
Nếu thành công:
Nội dung file
Tham số đầu tiên luôn là Error.
Đây được gọi là:
Error-first Callback
Callback nhiều cấp
Ví dụ
login(() => {
loadProfile(() => {
loadOrders(() => {
console.log("Hoàn thành");
});
});
});
Nhìn vẫn còn đơn giản.
Nhưng nếu dự án lớn:
login(() => {
profile(() => {
avatar(() => {
order(() => {
payment(() => {
history(() => {
message(() => {
notify(() => {
});
});
});
});
});
});
});
});
Code bắt đầu lệch sang bên phải rất nhiều.
Callback Hell
Đây là hiện tượng nhiều Callback lồng nhau.
login()
↓
profile()
↓
order()
↓
payment()
↓
email()
↓
log()
↓
notification()
Hay còn được gọi vui là:
Pyramid of Doom (Kim tự tháp địa ngục).
Nhược điểm của Callback Hell
Khó đọc.
Khó bảo trì.
Debug khó.
Dễ quên xử lý lỗi.
Khó tái sử dụng.
Đó là lý do Promise và Async/Await trở thành lựa chọn phổ biến trong JavaScript hiện đại.
Callback có phải luôn xấu?
Không.
Nhiều API của Node.js vẫn dùng Callback vì:
Đơn giản.
Hiệu năng tốt.
Không cần tạo Promise.
Ví dụ
fs.readFile(
"data.txt",
"utf8",
callback
);
Hoặc
server.listen(
3000,
() => {
console.log("Server running");
}
);
Callback và Event
Hai khái niệm này thường bị nhầm.
Callback
download(() => {
console.log("Xong");
});
Một Callback.
Event
event.on(
"message",
() => {
console.log("Có tin nhắn");
}
);
Có thể có nhiều Listener.
Callback và Promise
Callback
readFile((err, data) => {
});
Promise
readFile()
.then(data => {
});
Promise giúp code phẳng hơn và dễ kết hợp nhiều tác vụ bất đồng bộ.
Callback và Async/Await
Promise
await readFile();
↓
Code nhìn gần giống:
const data =
docFile();
Dễ đọc hơn rất nhiều.
Ví dụ thực tế
Giả sử tải thông tin người dùng.
function getUser(callback){
setTimeout(()=>{
callback({
name:"An"
});
},1000);
}
getUser((user)=>{
console.log(user.name);
});
↓
An
Mặc dù mất 1 giây để lấy dữ liệu, chương trình vẫn không bị chặn.
Luồng hoạt động
Gọi hàm
↓
Thực hiện công việc
↓
Hoàn thành
↓
Gọi Callback
Đây chính là cơ chế nền tảng của rất nhiều API bất đồng bộ trong Node.js.
Những lỗi thường gặp
Quên xử lý Error
Sai
fs.readFile(
"data.txt",
(err,data)=>{
console.log(data);
}
);
Nên
if(err){
return;
}
trước khi sử dụng data.
Callback Hell
A(()=>{
B(()=>{
C(()=>{
D(()=>{
});
});
});
});
Nên cân nhắc Promise hoặc Async/Await khi chuỗi xử lý trở nên phức tạp.
Gọi Callback nhiều lần
callback();
callback();
Có thể gây lỗi logic hoặc khiến kết quả bị xử lý lặp lại ngoài ý muốn.
Quên gọi Callback
function test(cb){
}
Nếu không gọi:
cb();
phần mã đang chờ Callback sẽ không bao giờ được thực thi.
Khi nào dùng Callback?
Phù hợp với:
API cũ.
Event.
Timer.
Stream.
HTTP Server.
Một số module lõi của Node.js.
Trong các dự án mới, Callback vẫn xuất hiện nhưng thường được thay thế hoặc bọc bởi Promise khi chuỗi xử lý trở nên phức tạp.
Bảng tổng hợp
| Khái niệm | Ý nghĩa |
|---|---|
| Callback | Hàm truyền vào hàm khác |
| Sync Callback | Chạy ngay |
| Async Callback | Chạy sau |
| Error-first Callback | (err, data) |
| Callback Hell | Callback lồng nhau |
| Promise | Giải quyết Callback Hell |
| Async/Await | Cú pháp hiện đại cho Promise |
Tổng kết
Callback là nền tảng của lập trình bất đồng bộ trong Node.js. Mặc dù ngày nay Promise và Async/Await được sử dụng phổ biến hơn, việc hiểu Callback vẫn rất quan trọng vì nhiều API cốt lõi của Node.js và không ít thư viện vẫn hỗ trợ hoặc sử dụng mô hình này.
Nắm vững Callback sẽ giúp bạn hiểu rõ cách hoạt động của Event Loop, Promise và Async/Await trong các bài học tiếp theo.
Bài tiếp theo
👉 Bài 12 — Promise
Bạn sẽ học:
Promise là gì.
Ba trạng thái của Promise.
resolve()vàreject().then(),catch()vàfinally().Vì sao Promise giúp mã nguồn dễ đọc, dễ bảo trì và giải quyết hiệu quả Callback Hell.




Không có nhận xét nào:
Đăng nhận xét