Hiểu rõ hơn về Promise trong Javascript - ES6
Promise là đề tài được tìm kiếm khá nhiều trong 2 năm gần đây, nhất là kể từ ngày AngularJS, NodeJS và các JS Framework khác ra đời bởi vì hầu hết chúng đều có sử dụng Promise để giải quyết xử lý bất đồng bộ (Async). Ở bài trước mình có giới thiệu sơ lược về cách sử dụng Promise rồi, tuy nhiên mình vãn đưa ra bài viết này bởi mình cần làm sáng tỏ một số vấn đề khác nữa.
Nếu bạn đang xem bài này lần đầu tiên thì mình nghĩ bạn nên quay lại đọc bài Promise trong Javascript sẽ giúp bạn dễ dàng follow hơn.
1. Ba trạng thái Pending - Fulfilled - Rejected
Ở bài trước mình có giới thiệu 3 trạng thái của Promise đó là pending, fulfilled và rejected, đây là ba trạng thái mà bất kì một Promise nào cũng phải có.
Pending
Pending là trạng thái khi bạn khởi tạo một Promise nhưng chưa thiết lập kết quả cho nó, tức là chưa sử dụng resolve và reject.
Bài viết này được đăng tại [free tuts .net]
var promise = new Promise(function(resolve, reject){ }); console.log(promise);
Kết quả:
Fulfilled
Fulfilled còn được gọi là resolved, đây là trạng thái của Promise đã thành công, trạng thái này xảy ra khi bạn sử dụng resolve.
var promise = new Promise(function(resolve, reject){ resolve(); }); console.log(promise);
Kết quả:
Rejected
Rejected là trạng thái thao tác thất bại, trạng thái này xảy ra khi bạn sử dụng reject. Khi bạn sử dụng reject thì bắt buộc phải khai báo hành động xử lý cho nó (tức sử dụng then
hoặc catch
).
var promise = new Promise(function(resolve, reject){ reject(); }); promise.catch(function(){ // Something }); console.log(promise);
Kết quả:
2. Thenable liên tiếp
Phương thức then()
có nhiệm vụ tiếp nhận kết quả trả về của promise và nó cũng return về một promise nên bạn có thể dùng nhiều lần liên tiếp với nhau.
var promise = new Promise(function(resolve, reject){ resolve(); }); promise.then(function(){ console.log(1); }) .then(function(){ console.log(2); }) .then(function(){ console.log(3); });
Kết quả:
Việc đặt thứ tự các phương thức rất quan trọng nhé các bạn. Nếu hành động của promise trả về là một Reject thì sẽ có hai trường hợp xảy ra như sau:
Trường hợp 1: Nếu trong phương thức then()
nào đó có sử dụng callback function Reject thì các phương thức then()
ở phía sau sẽ là một Fulfilled, nghĩa là nó sẽ chạy ở callback function Resolve.
var promise = new Promise(function(resolve, reject){ reject(); }); promise.then(function(){ console.log(1); }) .then(function(){ console.log(2); }, function(){ console.log('Error!') }) .then(function(){ console.log(3); });
Kết quả:
Nếu chiếu theo quy luật thì ở biến promise
mình đã sử dụng Reject
nên suy ra ở then()
tham số callback thứ hai sẽ hoạt động. Điều này hoàn toàn đúng với phương thức then()
thứ hai, vì vậy nó in ra chư Error!. Tuy nhiên nhảy qua phương thức then()
thứ tư thì nó lại chạy phần callback Resolve nên in ra số 3.
Trường hợp 2: Bạn sử dụng catch
để bắt lỗi, lúc này chỉ có phương thức catch()
là hoạt động.
var promise = new Promise(function(resolve, reject){ reject(); }); promise.then(function(){ console.log(1); }) .then(function(){ console.log(2); }) .then(function(){ console.log(3); }) .catch(function(){ console.log('Error!') });
Kết quả:
Ôi tới đây thì mình đã mắc một sai lầm nghiêm trọng. Thật ra thì hai trường hợp kia chỉ là lý luận theo hướng mắt thấy tai nghe nên nó không đúng đâu các bạn nhé, vấn đề nằm ở câu chốt dưới này :)
Khi sử dụng thenable liên tiếp thì kết quả return của phương thức
then()
hiện tại sẽ quyết định trạng thái của phương thứcthen()
tiếp theo. Ví dụthen()
phía trên return về một Promise Rejected thìthen()
phía dưới sẽ nhận một trang thái Rejected.
Mình sẽ lấy một ví dụ như sau:
asyncPromise1() .then(function () { return asyncPromise2(); }) .then(function () { return asyncPromise3(); }) .catch(function (err) { return asyncRecovery1(); }) .then(function () { return asyncPromise4(); }, function (err) { return asyncRecovery2(); } ) .catch(function (err) { console.log("Đừng lo lắng gì cả :)"); }) .then(function () { console.log("Mọi thứ đã xong!"); });
Kết quả của đoạn code đó phụ thuộc vào các Async mà nó return về. Và sau đây là bảng mô tả các trường hợp xảy ra.
Trong hình này đường màu xanh mô tả cho Async Promise return về một Fulfilled và màu tím mô tả cho Asycn Promise return về một Rejected.
Lấy một ví dụ chạy thực luôn cho các bạn xem nhé.
var promise = new Promise(function(resolve, reject){ resolve(); }); promise .then(function(){ return new Promise(function(resolve, reject){ reject(); }); }) .then(function(){ console.log('Success!'); }) .catch(function(){ console.log('Error!'); });
Kết quả:
Vì then()
thứ nhất return về một Reject Promise nên then()
thứ hai không chạy, và trạng thái bây giờ là Rejected nên catche sẽ được chạy => in ra chữ Error!.
Vậy là hết rồi đó !
3. Lời kết
Rõ ràng Promise trong Javascript nói chung và trong ES6 nói riêng rất phức tạp phải không các bạn, hôm nay mình mất đúng một ngày mới biên soạn xong hai bài này đấy :)
Việc hiểu nguyên tắc hoạt động của Promise rất quan trọng, nếu không bạn sẽ rất dễ mắc phải các lỗi về Callback Hell đấy. Cuối cùng chúc bạn học tốt và luôn ủng hộ freetuts.net nhé.