Hướng Dẫn Tự Xây Dựng Promise Trong JavaScript Đúng Chuẩn Promises/A+
Trong bài viết hôm nay, mình sẽ chia sẻ quá trình tự xây dựng một Promise trong JavaScript từ đầu, theo chuẩn Promises/A+. Đây không chỉ là một bài tập kiểm tra khả năng lập trình của bản thân mà còn là cơ hội để chia sẻ những gì mình học được tới các bạn đam mê lập trình. Hãy cùng bắt đầu nào!
Tạo Class Promise Đầu Tiên
Chúng ta sẽ gọi Promise tự tạo này là NotNow vì các tên hấp dẫn như will, future, hay later đã có nhiều package khác sử dụng. Để bắt đầu, hãy định nghĩa một số trạng thái cùng class NotNow:
javascript
const PENDING = "pending";
const FULFILLED = "fulfilled";
class NotNow {
#state = PENDING;
#onFulfilleds = [];
#value;
constructor(fn) {
fn(this.#fulfill.bind(this));
}
#fulfill(value) {
this.#state = FULFILLED;
this.#value = value;
this.#onFulfilleds.forEach((fn) => fn(value));
}
then(onFulfilled) {
this.#addOnFulfilled(onFulfilled);
}
#addOnFulfilled(onFulfilled) {
if (this.#state === PENDING) {
this.#onFulfilleds.push(onFulfilled);
} else if (this.#state === FULFILLED) {
onFulfilled(this.#value);
}
}
}
Trong constructor, chúng ta sẽ thực thi hàm được truyền vào, với phương thức #fulfill
hoạt động như một callback để trả giá trị về. Hàm #fulfill
sẽ lưu lại giá trị trả về và kích hoạt tất cả callback với giá trị đã lưu.
Kiểm Tra Promise Đơn Giản
Hãy kiểm tra NotNow với đoạn mã dưới:
javascript
const notNow = new NotNow((fulfill) => {
setTimeout(() => fulfill(2), 2000);
});
notNow.then((value) => console.log(`callback 1, fulfilled with ${value}`));
notNow.then((value) => console.log(`callback 2, fulfilled with ${value}`));
Sau 2 giây, kết quả sẽ là:
callback 1, fulfilled with 2
callback 2, fulfilled with 2
Thực Thi Callback Một Cách Bất Đồng Bộ
Để đảm bảo tính nhất quán, các callback sẽ được thực thi một cách bất đồng bộ. Do đó, chúng ta sẽ điều chỉnh lại hàm #fulfill
để sử dụng queueMicrotask
như sau:
javascript
queueMicrotask(() => this.#onFulfilleds.forEach((fn) => fn(value)));
Tạo Promise Mới Từ Callback
Mỗi lần khi thêm callback vào, chúng ta thực chất đang tạo ra một Promise mới. Để thực hiện điều này, chúng ta sẽ điều chỉnh phương thức then
để trả về một Promise mới mỗi khi nhận được kết quả. Dưới đây là đoạn mã đã được cập nhật:
javascript
class NotNow {
//...
then(onFulfilled) {
return new Promise((nextFulfill) => {
this.#addOnFulfilled((value) => {
const nextValue = onFulfilled(value);
nextFulfill(nextValue);
});
});
}
}
Kiểm Tra Ràng Buộc Promise
Chúng ta có thể nối tiếp nhiều phương thức then
, và các giá trị trả về sẽ được xử lý liên tục:
javascript
new NotNow((fulfill) => {
setTimeout(() => fulfill(2), 2000);
})
.then((value) => value * 2)
.then((value) => console.log(value));
Khi Promise nhận được giá trị 2, sau đó giá trị cuối cùng in ra sẽ là 4.
Xử Lý Promise Lồng
Một đặc điểm thú vị của Promise là nếu bạn trả về một Promise từ một Promise khác, nó sẽ tự động nhận giá trị. Đây là cách chúng ta kiểm thử tính năng này:
javascript
const a = new Promise((fulfill) => fulfill(2));
const b = new Promise((fulfill) => fulfill(a));
b.then((value) => console.log(value));
Kết quả vẫn là 2, cho thấy khái niệm này hoạt động chính xác.
Kiểm Tra Với Async/Await
Class NotNow cũng hỗ trợ async/await một cách hoàn hảo. Bạn có thể dùng nó như sau:
javascript
const a = await new NotNow((fulfill) => {
setTimeout(() => fulfill(5), 2000);
});
console.log(a);
Kết Luận
Trong bài viết này, mình đã giới thiệu cách xây dựng một Promise từ đầu theo chuẩn Promises/A+. Mặc dù bài viết chưa đề cập đến xử lý lỗi và một số phương thức bổ sung như all
, allSettled
, any
, race
, nhưng hy vọng bạn sẽ thấy nó hữu ích. Nếu bạn có bất kỳ câu hỏi hay ý kiến nào, hãy để lại ý kiến trong phần bình luận nhé!
source: viblo