lt;blockquotegt;lt;pgt;lt;emgt;quot;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.quot;lt;/emgt;lt;/pgt;lt;/blockquotegt;lt;hr /gt;lt;h1gt;🎯 Mục tiêu bài họclt;/h1gt;lt;pgt;Sau bài này bạn sẽ:lt;/pgt;lt;ulgt;lt;ligt;lt;pgt;Hiểu Callback là gì.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Phân biệt Callback đồng bộ và bất đồng bộ.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Biết cách truyền Callback vào hàm.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Hiểu Callback Hell là gì.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Biết khi nào nên dùng Callback.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Hiểu vì sao Promise và Async/Await ra đời.lt;/pgt;lt;div class=quot;separatorquot; style=quot;clear: both; text-align: center;quot;gt;lt;a href=quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHP2Vor6Y2DkaR0PoR6b8xrleR6doJdsA7g3XCxF4GSmzcaE4KQuisNoWyyFOyxnI1wafBkQaqtcqu33ztUEAl4YoW-p_bQ3NKa7tbzfcAugTN2kXijw_vVY2wjX3Y7_ha65byS_uZpJmPBICUNgEL3tgJ_1r-CeW0l44xrjbvd_gNL343lQw1mi93RQg/s1536/bai11.pngquot; style=quot;margin-left: 1em; margin-right: 1em;quot;gt;lt;img border=quot;0quot; data-original-height=quot;1024quot; data-original-width=quot;1536quot; height=quot;213quot; src=quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHP2Vor6Y2DkaR0PoR6b8xrleR6doJdsA7g3XCxF4GSmzcaE4KQuisNoWyyFOyxnI1wafBkQaqtcqu33ztUEAl4YoW-p_bQ3NKa7tbzfcAugTN2kXijw_vVY2wjX3Y7_ha65byS_uZpJmPBICUNgEL3tgJ_1r-CeW0l44xrjbvd_gNL343lQw1mi93RQg/s320/bai11.pngquot; width=quot;320quot; /gt;lt;/agt;lt;spangt;lt;a name=#39;more#39;gt;lt;/agt;lt;/spangt;lt;/divgt;lt;pgt;lt;/pgt;lt;/ligt;lt;/ulgt;lt;hr /gt;lt;h1gt;Callback là gì?lt;/h1gt;lt;pgt;lt;stronggt;Callbacklt;/stronggt; là một lt;stronggt;hàm được truyền vào làm tham số của một hàm kháclt;/stronggt; để được gọi lại (call back) khi cần.lt;/pgt;lt;pgt;Ví dụ đơn giản:lt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;function hello(callback) {
console.log(quot;Xin chàoquot;);
callback();
}
hello(() =gt; {
console.log(quot;Node.jsquot;);
});
lt;/codegt;lt;/pregt;lt;pgt;Kết quảlt;/pgt;lt;pregt;lt;code class=quot;language-textquot;gt;Xin chào
Node.js
lt;/codegt;lt;/pregt;lt;pgt;Ở đây:lt;/pgt;lt;ulgt;lt;ligt;lt;pgt;lt;code inline=quot;quot;gt;hello()lt;/codegt; là hàm chính.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Hàm mũi tên được truyền vào chính là Callback.lt;/pgt;lt;/ligt;lt;/ulgt;lt;hr /gt;lt;h1gt;Vì sao cần Callback?lt;/h1gt;lt;pgt;Giả sử bạn cần:lt;/pgt;lt;olgt;lt;ligt;lt;pgt;Đọc file.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Sau khi đọc xong mới xử lý dữ liệu.lt;/pgt;lt;/ligt;lt;/olgt;lt;pgt;Không thể:lt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;readFile();
xuLy();
lt;/codegt;lt;/pregt;lt;pgt;vì chưa chắc file đã đọc xong.lt;/pgt;lt;pgt;Thay vào đó:lt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;readFile(() =gt; {
xuLy();
});
lt;/codegt;lt;/pregt;lt;pgt;Chỉ khi hoàn thành, Callback mới được gọi.lt;/pgt;lt;hr /gt;lt;h1gt;Callback đồng bộlt;/h1gt;lt;pgt;Ví dụlt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;function tinhTong(a, b, callback) {
const tong = a + b;
callback(tong);
}
console.log(quot;Bắt đầuquot;);
tinhTong(5, 8, (kq) =gt; {
console.log(kq);
});
console.log(quot;Kết thúcquot;);
lt;/codegt;lt;/pregt;lt;pgt;↓lt;/pgt;lt;pregt;lt;code class=quot;language-textquot;gt;13
lt;/codegt;lt;/pregt;lt;pgt;Ở đây Callback được gọi lt;stronggt;ngay lập tứclt;/stronggt;, nên đây là Callback đồng bộ (Synchronous Callback).lt;/pgt;lt;hr /gt;lt;h1gt;Callback bất đồng bộlt;/h1gt;lt;pgt;Ví dụlt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;function tinhTong(a, b, callback) {
const tong = a + b;
setTimeout(() =gt; {
callback(tong);
}, 2000);
}
console.log(quot;Bắt đầuquot;);
tinhTong(5, 8, (kq) =gt; {
console.log(kq);
});
console.log(quot;Kết thúcquot;);
lt;/codegt;lt;/pregt;lt;pgt;↓lt;/pgt;lt;pregt;lt;code class=quot;language-textquot;gt;Đang chờ...
(sau 2 giây)
Đã xong
lt;/codegt;lt;/pregt;lt;pgt;Callback chỉ chạy sau khi hết thời gian chờ.lt;/pgt;lt;hr /gt;lt;h1gt;Callback với fslt;/h1gt;lt;pgt;Ví dụlt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;import fs from quot;node:fsquot;;
fs.readFile(
quot;data.txtquot;,
quot;utf8quot;,
(err, data) =gt; {
if (err) {
console.log(err);
return;
}
console.log(data);
}
);
lt;/codegt;lt;/pregt;lt;pgt;↓lt;/pgt;lt;pregt;lt;code class=quot;language-textquot;gt;Xin chào Node.js
lt;/codegt;lt;/pregt;lt;pgt;Đây là kiểu Callback rất phổ biến trong Node.js.lt;/pgt;lt;hr /gt;lt;h1gt;Error-first Callbacklt;/h1gt;lt;pgt;Phần lớn API của Node.js sử dụng quy ước:lt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;(err, data)
lt;/codegt;lt;/pregt;lt;pgt;Ví dụlt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;fs.readFile(
quot;abc.txtquot;,
quot;utf8quot;,
(err, data) =gt; {
if (err) {
console.log(err);
return;
}
console.log(data);
}
);
lt;/codegt;lt;/pregt;lt;pgt;Nếu lỗi:lt;/pgt;lt;pregt;lt;code class=quot;language-textquot;gt;ENOENT
lt;/codegt;lt;/pregt;lt;pgt;Nếu thành công:lt;/pgt;lt;pregt;lt;code class=quot;language-textquot;gt;Nội dung file
lt;/codegt;lt;/pregt;lt;pgt;Tham số đầu tiên luôn là lt;stronggt;Errorlt;/stronggt;.lt;/pgt;lt;pgt;Đây được gọi là:lt;/pgt;lt;blockquotegt;lt;pgt;lt;stronggt;Error-first Callbacklt;/stronggt;lt;/pgt;lt;/blockquotegt;lt;hr /gt;lt;h1gt;Callback nhiều cấplt;/h1gt;lt;pgt;Ví dụlt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;function login(callback) {
console.log(quot;Đang đăng nhập...quot;);
setTimeout(() =gt; {
console.log(quot;Đăng nhập thành côngquot;);
callback();
}, 2000);
}
function loadProfile(callback) {
console.log(quot;Đang tải thông tin...quot;);
setTimeout(() =gt; {
console.log(quot;Đã tải Profilequot;);
callback();
}, 1500);
}
function loadOrders(callback) {
console.log(quot;Đang tải đơn hàng...quot;);
setTimeout(() =gt; {
console.log(quot;Đã tải đơn hàngquot;);
callback();
}, 1000);
}
// Callback nhiều cấp
login(() =gt; {
loadProfile(() =gt; {
loadOrders(() =gt; {
console.log(quot;Hoàn thànhquot;);
});
});
});
lt;/codegt;lt;/pregt;lt;pgt;Nhìn vẫn còn đơn giản.lt;/pgt;lt;pgt;Nhưng nếu dự án lớn:lt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;login(() =gt; {
profile(() =gt; {
avatar(() =gt; {
order(() =gt; {
payment(() =gt; {
history(() =gt; {
message(() =gt; {
notify(() =gt; {
});
});
});
});
});
});
});
});
lt;/codegt;lt;/pregt;lt;pgt;Code bắt đầu lệch sang bên phải rất nhiều.lt;/pgt;lt;hr /gt;lt;h1gt;Callback Helllt;/h1gt;lt;pgt;Đây là hiện tượng nhiều Callback lồng nhau.lt;/pgt;lt;pregt;lt;code class=quot;language-textquot;gt;login()
↓
profile()
↓
order()
↓
payment()
↓
email()
↓
log()
↓
notification()
lt;/codegt;lt;/pregt;lt;pgt;Hay còn được gọi vui là:lt;/pgt;lt;blockquotegt;lt;pgt;lt;stronggt;Pyramid of Doom (Kim tự tháp địa ngục).lt;/stronggt;lt;/pgt;lt;/blockquotegt;lt;hr /gt;lt;h1gt;Nhược điểm của Callback Helllt;/h1gt;lt;ulgt;lt;ligt;lt;pgt;Khó đọc.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Khó bảo trì.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Debug khó.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Dễ quên xử lý lỗi.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Khó tái sử dụng.lt;/pgt;lt;/ligt;lt;/ulgt;lt;pgt;Đó là lý do Promise và Async/Await trở thành lựa chọn phổ biến trong JavaScript hiện đại.lt;/pgt;lt;hr /gt;lt;h1gt;Callback có phải luôn xấu?lt;/h1gt;lt;pgt;Không.lt;/pgt;lt;pgt;Nhiều API của Node.js vẫn dùng Callback vì:lt;/pgt;lt;ulgt;lt;ligt;lt;pgt;Đơn giản.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Hiệu năng tốt.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Không cần tạo Promise.lt;/pgt;lt;/ligt;lt;/ulgt;lt;pgt;Ví dụlt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;fs.readFile(
quot;data.txtquot;,
quot;utf8quot;,
callback
);
lt;/codegt;lt;/pregt;lt;pgt;Hoặclt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;import http from quot;node:httpquot;;
const server = http.createServer((req, res) =gt; {
res.end(quot;Hello Node.jsquot;);
});
server.listen(3000, () =gt; {
console.log(quot;Server runningquot;);
});
lt;/codegt;lt;/pregt;lt;hr /gt;lt;h1gt;Callback và Eventlt;/h1gt;lt;pgt;Hai khái niệm này thường bị nhầm.lt;/pgt;lt;pgt;Callbacklt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;download(() =gt; {
console.log(quot;Xongquot;);
});
lt;/codegt;lt;/pregt;lt;pgt;Một Callback.lt;/pgt;lt;hr /gt;lt;pgt;Eventlt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;event.on(
quot;messagequot;,
() =gt; {
console.log(quot;Có tin nhắnquot;);
}
);
lt;/codegt;lt;/pregt;lt;pgt;Có thể có nhiều Listener.lt;/pgt;lt;hr /gt;lt;h1gt;Callback và Promiselt;/h1gt;lt;pgt;Callbacklt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;readFile((err, data) =gt; {
});
lt;/codegt;lt;/pregt;lt;pgt;Promiselt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;readFile()
.then(data =gt; {
});
lt;/codegt;lt;/pregt;lt;pgt;Promise giúp code phẳng hơn và dễ kết hợp nhiều tác vụ bất đồng bộ.lt;/pgt;lt;hr /gt;lt;h1gt;Callback và Async/Awaitlt;/h1gt;lt;pgt;Promiselt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;await readFile();
lt;/codegt;lt;/pregt;lt;pgt;↓lt;/pgt;lt;pgt;Code nhìn gần giống:lt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;const data =
docFile();
lt;/codegt;lt;/pregt;lt;pgt;Dễ đọc hơn rất nhiều.lt;/pgt;lt;hr /gt;lt;h1gt;Ví dụ thực tếlt;/h1gt;lt;pgt;Giả sử tải thông tin người dùng.lt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;function getUser(callback){
setTimeout(()=gt;{
callback({
name:quot;Anquot;
});
},1000);
}
getUser((user)=gt;{
console.log(user.name);
});
lt;/codegt;lt;/pregt;lt;pgt;↓lt;/pgt;lt;pregt;lt;code class=quot;language-textquot;gt;An
lt;/codegt;lt;/pregt;lt;pgt;Mặc dù mất 1 giây để lấy dữ liệu, chương trình vẫn không bị chặn.lt;/pgt;lt;hr /gt;lt;h1gt;Luồng hoạt độnglt;/h1gt;lt;pregt;lt;code class=quot;language-textquot;gt;Gọi hàm
↓
Thực hiện công việc
↓
Hoàn thành
↓
Gọi Callback
lt;/codegt;lt;/pregt;lt;pgt;Đây chính là cơ chế nền tảng của rất nhiều API bất đồng bộ trong Node.js.lt;/pgt;lt;hr /gt;lt;h1gt;Những lỗi thường gặplt;/h1gt;lt;h2gt;Quên xử lý Errorlt;/h2gt;lt;pgt;Sailt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;fs.readFile(
quot;data.txtquot;,
(err,data)=gt;{
console.log(data);
}
);
lt;/codegt;lt;/pregt;lt;pgt;Nênlt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;if(err){
return;
}
lt;/codegt;lt;/pregt;lt;pgt;trước khi sử dụng lt;code inline=quot;quot;gt;datalt;/codegt;.lt;/pgt;lt;hr /gt;lt;h2gt;Callback Helllt;/h2gt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;A(()=gt;{
B(()=gt;{
C(()=gt;{
D(()=gt;{
});
});
});
});
lt;/codegt;lt;/pregt;lt;pgt;Nên cân nhắc Promise hoặc Async/Await khi chuỗi xử lý trở nên phức tạp.lt;/pgt;lt;hr /gt;lt;h2gt;Gọi Callback nhiều lầnlt;/h2gt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;callback();
callback();
lt;/codegt;lt;/pregt;lt;pgt;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.lt;/pgt;lt;hr /gt;lt;h2gt;Quên gọi Callbacklt;/h2gt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;function test(cb){
}
lt;/codegt;lt;/pregt;lt;pgt;Nếu không gọi:lt;/pgt;lt;pregt;lt;code class=quot;language-javascriptquot;gt;cb();
lt;/codegt;lt;/pregt;lt;pgt;phần mã đang chờ Callback sẽ không bao giờ được thực thi.lt;/pgt;lt;hr /gt;lt;h1gt;Khi nào dùng Callback?lt;/h1gt;lt;pgt;Phù hợp với:lt;/pgt;lt;ulgt;lt;ligt;lt;pgt;API cũ.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Event.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Timer.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Stream.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;HTTP Server.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Một số module lõi của Node.js.lt;/pgt;lt;/ligt;lt;/ulgt;lt;pgt;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.lt;/pgt;lt;hr /gt;lt;h1gt;Bảng tổng hợplt;/h1gt;lt;tablegt;lt;theadgt;lt;trgt;lt;thgt;Khái niệmlt;/thgt;lt;thgt;Ý nghĩalt;/thgt;lt;/trgt;lt;/theadgt;lt;tbodygt;lt;trgt;lt;tdgt;Callbacklt;/tdgt;lt;tdgt;Hàm truyền vào hàm kháclt;/tdgt;lt;/trgt;lt;trgt;lt;tdgt;Sync Callbacklt;/tdgt;lt;tdgt;Chạy ngaylt;/tdgt;lt;/trgt;lt;trgt;lt;tdgt;Async Callbacklt;/tdgt;lt;tdgt;Chạy sault;/tdgt;lt;/trgt;lt;trgt;lt;tdgt;Error-first Callbacklt;/tdgt;lt;tdgt;lt;code inline=quot;quot;gt;(err, data)lt;/codegt;lt;/tdgt;lt;/trgt;lt;trgt;lt;tdgt;Callback Helllt;/tdgt;lt;tdgt;Callback lồng nhault;/tdgt;lt;/trgt;lt;trgt;lt;tdgt;Promiselt;/tdgt;lt;tdgt;Giải quyết Callback Helllt;/tdgt;lt;/trgt;lt;trgt;lt;tdgt;Async/Awaitlt;/tdgt;lt;tdgt;Cú pháp hiện đại cho Promiselt;/tdgt;lt;/trgt;lt;/tbodygt;lt;/tablegt;lt;hr /gt;lt;h1gt;Tổng kếtlt;/h1gt;lt;pgt;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.lt;/pgt;lt;pgt;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.lt;/pgt;lt;hr /gt;lt;h1gt;Bài tiếp theolt;/h1gt;lt;pgt;👉 lt;stronggt;Bài 12 — Promiselt;/stronggt;lt;/pgt;lt;pgt;Bạn sẽ học:lt;/pgt;lt;ulgt;lt;ligt;lt;pgt;Promise là gì.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Ba trạng thái của Promise.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;lt;code inline=quot;quot;gt;resolve()lt;/codegt; và lt;code inline=quot;quot;gt;reject()lt;/codegt;.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;lt;code inline=quot;quot;gt;then()lt;/codegt;, lt;code inline=quot;quot;gt;catch()lt;/codegt; và lt;code inline=quot;quot;gt;finally()lt;/codegt;.lt;/pgt;lt;/ligt;lt;ligt;lt;pgt;Vì sao Promise giúp mã nguồn dễ đọc, dễ bảo trì và giải quyết hiệu quả Callback Hell.lt;/pgt;lt;/ligt;lt;/ulgt;