Thuộc phần 3: Asynchronous Programming
Mục tiêu bài học
Sau bài này, bạn sẽ hiểu:
Promise là gì.
Vì sao Promise ra đời.
Ba trạng thái của Promise.
Cách tạo Promise.
.then().catch().finally()Promise Chaining.
Promise trong Node.js thực tế.
Callback Hell
Ở bài trước chúng ta đã học Callback.
Ví dụ:
login(function(user){
getProfile(user, function(profile){
getPosts(profile, function(posts){
console.log(posts);
});
});
});
Khi chương trình lớn lên, callback lồng callback sẽ khiến code:
Khó đọc
Khó bảo trì
Khó bắt lỗi
Đây gọi là:
Callback Hell
Hay còn gọi vui là:
Pyramid of Doom
Ví dụ:
step1(function(){
step2(function(){
step3(function(){
step4(function(){
step5(function(){
});
});
});
});
});
Nhìn giống một kim tự tháp.
Đây chính là lý do Promise ra đời.
Promise là gì?
Promise là một đối tượng đại diện cho kết quả của một tác vụ bất đồng bộ, có thể thành công hoặc thất bại trong tương lai.
Hiểu đơn giản:
Promise giống như một "tờ giấy hứa".
Ví dụ:
Bạn đặt hàng online.
Ngay lúc đặt:
Chưa có hàng
Chưa biết khi nào tới
Shop chỉ hứa:
"Tôi sẽ giao."
Sau vài ngày:
giao thành công
hoặc giao thất bại
Đó chính là Promise.
Promise có 3 trạng thái
Pending
Đang chờ xử lý.
Ví dụ:
Đang tải dữ liệu...
Fulfilled
Thành công.
Ví dụ:
Đã lấy dữ liệu thành công.
Rejected
Thất bại.
Ví dụ:
Mất kết nối.
Minh họa:
Pending
/ \
/ \
Fulfilled Rejected
Cú pháp Promise
const promise = new Promise(function(resolve, reject){
});
Hoặc dùng Arrow Function:
const promise = new Promise((resolve, reject) => {
});
Trong đó:
resolve() → thành công
reject() → thất bại
Promise thành công
Ví dụ:
const promise = new Promise((resolve, reject) => {
resolve("Xin chào Node.js");
});
Lấy kết quả:
promise.then(function(data){
console.log(data);
});
Kết quả:
Xin chào Node.js
Promise thất bại
const promise = new Promise((resolve, reject) => {
reject("Có lỗi xảy ra");
});
Xử lý lỗi:
promise.catch(function(error){
console.log(error);
});
Kết quả:
Có lỗi xảy ra
then()
.then() chạy khi Promise thành công.
Ví dụ:
const promise = new Promise((resolve) => {
resolve(100);
});
promise.then((number) => {
console.log(number);
});
Kết quả:
100
catch()
.catch() chạy khi Promise bị lỗi.
Ví dụ:
const promise = new Promise((resolve, reject) => {
reject("Server Error");
});
promise.catch((error)=>{
console.log(error);
});
Kết quả:
Server Error
finally()
.finally() luôn chạy dù thành công hay thất bại.
Ví dụ:
promise
.then(data=>{
console.log(data);
})
.catch(err=>{
console.log(err);
})
.finally(()=>{
console.log("Hoàn thành.");
});
Kết quả:
Hoàn thành.
Rất hữu ích khi:
Tắt Loading
Đóng Database
Giải phóng tài nguyên
Ghi log
Promise giả lập thời gian chờ
Ví dụ:
const promise = new Promise((resolve)=>{
setTimeout(()=>{
resolve("Đã xong.");
},3000);
});
Lấy dữ liệu:
promise.then((data)=>{
console.log(data);
});
Sau 3 giây:
Đã xong.
Promise Chaining
Promise có thể nối tiếp nhau.
Ví dụ:
Promise.resolve(5)
.then(number=>{
return number * 2;
})
.then(number=>{
return number + 10;
})
.then(number=>{
console.log(number);
});
Kết quả:
20
Mỗi .then() nhận kết quả của .then() trước đó.
Trả về Promise trong then()
Ví dụ:
Promise.resolve(10)
.then(number=>{
return new Promise((resolve)=>{
resolve(number * 3);
});
})
.then(result=>{
console.log(result);
});
Kết quả:
30
Promise sẽ tự động chờ Promise mới hoàn thành trước khi chuyển sang .then() tiếp theo.
Promise với setTimeout()
Ví dụ:
function sleep(ms){
return new Promise((resolve)=>{
setTimeout(resolve, ms);
});
}
sleep(2000)
.then(()=>{
console.log("2 giây đã trôi qua.");
});
Đây là một mẫu rất phổ biến trong lập trình Node.js.
Promise trong File System
Từ Node.js hiện đại, nhiều API đã hỗ trợ Promise.
Ví dụ:
import { readFile } from "node:fs/promises";
readFile("hello.txt", "utf8")
.then(data=>{
console.log(data);
})
.catch(err=>{
console.log(err);
});
Không còn phải truyền callback như trước.
Promise trong Fetch API
Node.js hiện đại đã tích hợp sẵn fetch().
Ví dụ:
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.log(error);
});
Quy trình xử lý:
fetch()
↓
Response
↓
response.json()
↓
Object JavaScript
Đây là cách gọi API rất phổ biến trong các ứng dụng Node.js và trình duyệt.
Promise.all()
Chạy nhiều Promise song song.
Ví dụ:
Promise.all([
Promise.resolve("Node.js"),
Promise.resolve("Express"),
Promise.resolve("Prisma")
])
.then(data=>{
console.log(data);
});
Kết quả:
[
'Node.js',
'Express',
'Prisma'
]
Nếu một Promise thất bại thì toàn bộ Promise.all() sẽ bị từ chối (reject).
Promise.race()
Trả về Promise hoàn thành đầu tiên.
Ví dụ:
Promise.race([
new Promise(resolve => setTimeout(() => resolve("A"), 3000)),
new Promise(resolve => setTimeout(() => resolve("B"), 1000))
])
.then(data => {
console.log(data);
});
Kết quả:
B
Promise.race() hữu ích khi cần giới hạn thời gian chờ hoặc chọn kết quả phản hồi nhanh nhất.
Promise.any()
Trả về Promise thành công đầu tiên.
Ví dụ:
Promise.any([
Promise.reject("Lỗi 1"),
Promise.resolve("Đã thành công"),
Promise.resolve("Dự phòng")
])
.then(result => {
console.log(result);
});
Kết quả:
Đã thành công
Nếu tất cả Promise đều thất bại thì Promise.any() mới phát sinh lỗi.
Promise.allSettled()
Chờ tất cả Promise kết thúc, bất kể thành công hay thất bại.
Ví dụ:
Promise.allSettled([
Promise.resolve("OK"),
Promise.reject("Lỗi")
])
.then(results => {
console.log(results);
});
Kết quả:
[
{ status: "fulfilled", value: "OK" },
{ status: "rejected", reason: "Lỗi" }
]
Rất phù hợp khi cần tổng hợp kết quả của nhiều tác vụ độc lập.
Khi nào nên dùng Promise?
Nên dùng Promise khi:
Đọc/Ghi file.
Gọi REST API.
Truy vấn cơ sở dữ liệu.
Upload file.
Xử lý nhiều tác vụ bất đồng bộ.
Làm việc với các thư viện hiện đại của Node.js.
Những lỗi thường gặp
❌ Quên xử lý lỗi bằng .catch().
promise.then(data => {
console.log(data);
});
Nếu Promise bị từ chối (reject), lỗi có thể không được xử lý.
❌ Không return giá trị trong .then().
Promise.resolve(5)
.then(number => {
number * 2;
})
.then(result => {
console.log(result);
});
Kết quả:
undefined
Cần viết:
return number * 2;
❌ Tạo Promise không cần thiết.
Ví dụ:
return new Promise((resolve) => {
resolve(data);
});
Có thể rút gọn:
return Promise.resolve(data);
Hoặc đơn giản:
return data;
Tổng kết
Trong bài học này, bạn đã nắm được cách Promise giúp giải quyết hạn chế của Callback bằng cách tổ chức mã nguồn rõ ràng và dễ bảo trì hơn. Bạn đã biết ba trạng thái của Promise, cách sử dụng .then(), .catch(), .finally(), kết hợp nhiều Promise với Promise.all(), Promise.race(), Promise.any(), Promise.allSettled(), cũng như ứng dụng Promise trong đọc file và gọi API.
Ở bài tiếp theo, chúng ta sẽ học Bài 13 — Async / Await, cú pháp hiện đại giúp viết mã bất đồng bộ theo phong cách gần giống mã đồng bộ, ngắn gọn và dễ đọc hơn.




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