Understanding Promise in Javascript - Part 1

I. Promise là gì ?


- Promise là một object đại diện cho kết quả của một tác vụ bất đồng bộ.
- Promise được trả về đồng bộ (ngay lập tức) từ một hàm không đồng bộ (fetch, API Browser, Node API...), khi xử lý các tác vụ không đồng bộ chúng ta có thể sử dụng callback nhưng cách tiếp cận đó gây nhiều khó khăn khi sử dụng (Bạn có thể tìm hiểu vấn đề callback hell), hãy hiểu đơn giản Promise như một wrapper object, nó wrapper các hàm bất đồng bộ để giúp chúng ta có thể làm việc với chúng dễ dàng hơn.
- Promise có thể có
  3 trạng thái :
+ Fulfilled : callback onFulfilled() của then method sẽ được invoked (trong trường hợp resolve đã được invoked trong Promise)
+ Rejected : callback onRejected() của then method sẽ được invoked (trong trường hợp rejected đã được invoked trong Promise)
+ Pending: chưa được giải quyết.
- Để hiểu được Promise cần tìm hiểu 2 phần là Creating PromiseHandle Promise


II. Creating Promise

Để tạo ra một Promise chúng ta sử dụng cú pháp sau :

 new Promise( /* executor */ function(resolve, reject) { ... } );

Bạn có thể thấy contructor function (ở trường hợp này chúng ta gọi contructor function là executor) chấp nhận một callback làm đầu vào, callback chấp nhận 2 tham số cũng đều là callback lần lượt  resolve (invoked để báo hiệu giải quyết lời hứa thành công) và resject (invoked để báo hiểu giải quyết lời hứa thất bại).

Promise thường được sử dụng để xử lý các hoạt động không đồng bộ như call API, communicate Database...chúng ta sẽ thực thi các tác vụ bất đồng bộ bên trong hàm executor, khi các hoạt động bất đồng bộ này diễn ra thành công thì chúng ta gọi hàm resolve, ngược lại thất bại thì chúng ta gọi hàm rejected.

Ví dụ :

var keepsHisWord;
keepsHisWord = true;
promise1 = new Promise(function(resolve, reject) {
if (keepsHisWord) {
resolve("The man likes to keep his word");
} else {
reject("The man doesnt want to keep his word");
}
});
console.log(promise1);



Như hình trên bạn sẽ thấy mỗi lời hứa có 2 hidden property (hidden property có nghĩa rằng bạn không thể sử dụng dấu . (dot notation) để truy cập nó được) là PromiseStatus (gọi tắc là status) và PromiseValue (gọi tắt là value).

Bởi vì Promise bên trên được giải quết ngay lập tức nên chúng ta không thấy được sự thay đổi trạng thái của nó, để theo dõi sự thay đổi trạng thái bạn hãy sử dụng setTimeout.

promise2 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve({
message: "The man likes to keep his word",
code: "aManKeepsHisWord"
});
}, 10 * 1000);
});
console.log(promise2);

Đoạn code bên trên tạo ra một Promise và sau 10s nó sẽ được giải quyết cả PromiseValue và PromiseStatus đều được cập nhật, hãy nhìn hình bên dưới có thể nhận xét rằng giá trị chúng ta pass vào hàm resolve sẽ là giá trị cuối cùng của promise đó.


Chúng ta đã tạo ra Promise với trạng thái Fulfilled (tức là resolve đã được gọi) vậy Promise ở trạng thái Rejected thì sao ?

const keepsHisWord = false;
promise3 = new Promise(function(resolve, reject) {
if (keepsHisWord) {
resolve("The man likes to keep his word");
} else {
reject("The man doesn't want to keep his word");
}
});
console.log(promise3);

Hãy xem kết quả : 



Kết quả ở trường hợp này cũng khá giống trường hợp bên trên chỉ khác mỗi Status của promise là rejected, điều đặc biệt ở dòng cuối cùng một lỗi được bắn ra => Điều này xảy ra khi một Promise ở trạng thái rejected nhưng không được catch (chúng ta sẽ nói về điều này thêm)

=> Vậy kết luận : Bạn có thể thấy rằng [[PromiseValue]] có thể có 3 giá trị là pending, resolve, rejected. Khi một lời hứa được khởi tạo [[PromiseValue]] của nó được đặt bằng Undefine [[PromiseStatus]] của nó là đươc đặt là Pending khi Promise đã được giải quyết (settled) . Khi Promise đã được giải quyết thì có nghĩa nó đã chuyển sang 1 trong 2 trạng thái Fulfilled hoặc Rejected lưu ý rằng một khi đã chuyển trạng thái Promise sẽ không bao giờ có thể thay đổi giá trị cũng như trạng thái nó một lần nữa.


Hiểu về Promise Object từ lý thuyết

Theo MDN : 
Promise là một đối tượng đại diện cho sự hoàn thành cuối cùng của một  tác vụ bất đồng bộ và kết quả của nó.

Promise object có phương thức tĩnh (static method) và phương thức nguyên mẫu (prototype method)
Nếu bạn chưa hiểu về static method cũng như prototype method hãy hiểu đơn giản rằng với static method bạn có thể gọi nó từ một contructor function (Promise) còn prototype method chỉ được gọi với một instance của Promise mà thôi.


Prototype Methods

Có 3 phương thức ở prototype mà bạn cần quan tâm đó là then, catch, finally  


Hãy nhớ rằng tất cả các phương thức then,catch,finally đều trả về một Promise với value là kết quả return từ hàm callback của những hàm then,catch,finally.

Như chúng ta đã biết khi một Promise được tạo ra nó sẽ ở trạng thái Pending. Một hoặc nhiều trong 3 phương thức này sẽ được thực thi khi Promise được giải quyết dựa vào Promise được Resolve hay Rejected.

Static Methods


Có 4 static method cần nhắc đến trong Promise 2 cái đầu giúp chúng ta tạo ra những Promise nhanh hơn với status ban đầu là Fulfilled hoặc Rejected 

Promise.reject(reason)
Giúp chúng ta tạo một Promise có status ban đầu là rejeteted và value là giá trị được truyền vào rejecte method

var promise3 = Promise.reject("Not interested");
promise3.then(function(value){
console.log("This will not run as it is a resolved promise. The resolved value is ", value);
});
promise3.catch(function(reason){
console.log("This run as it is a rejected promise. The reason is ", reason);
});



Promise.resolve(value)

Giúp chúng ta tạo nhanh một Promise với status ban đầu là resolve và giá trị của Promise được truyền vào method resolve.  
var promise4 = Promise.resolve(1);
promise4.then(function(value){
console.log("This will run as it is a resovled promise. The resolved value is ", value);
});
promise4.catch(function(reason){
console.log("This will not run as it is a resolved promise", reason);
});
Promise.All
Promise.All(iterable) chấp nhận đầu vào là một iterable các Promise và trả về một Promise duy nhất khi tất cả các Promise trong iterable đầu vào đã ở trạng thái settled hoặc trong iterable không chứa Promise nào. Promise được trả về bởi method .All sẽ bị reject cùng với lý do mà Promise đầu tiên trong iterable bị reject.
=> Chú ý rằng trong Promise.All tất cả các lời hứa được thực thi song song nhưng hàm chỉ trả về kết quả khi tất cả các Promise trong iterable được giải quyết xong.
Promise.Race
Promise.race(iterable) trả về một Promise ngay khi một Promise bất kì trong iterable được resolve hoặc rejecte với giá trị cùng với giá trị của Promise đầu tiên được settled đó.

III.  Handle Error

Bạn có thể thấy rằng trong rất nhiều tài liệu Promise, then method có cả handleSuccess(onFulfiled) và handleError(onRejected) nhưng khi triển khai code hầu hết chúng ta đều xử lý lỗi bằng catch method ?


Hãy quan sát hình bên trên, nếu Promise bắn ra lỗi hàm onRejected của then có thể catch được lỗi nhưng nếu lỗi phát sinh từ hàm onFulfilled thì sao lúc này onRejected không thể catch được lỗi do nếu hàm onRejected đã được invoked thì hàm onFulfilled sẽ không được invoked và ngược lại. Vậy giải pháp là gì ?


Nếu chúng ta xử lý lỗi bằng catch method như hình, trình xử lý lỗi sẽ handle được cả lỗi phát sinh ra ở Promise lẫn lỗi phát sinh ra ở hàm onFulfilled.

Nhận xét

Bài đăng phổ biến