Promise
Promise là một special object được tích hợp trong JavaScript. Promise đóng vai trò là một placeholder cho dữ liệu chúng ta mong muốn nhận lại được từ hoạt động nền của web browser feature. Trong JavaScript, Promise được dùng để xử lý những vấn đề liên quan đến bất đồng bộ (asynchronous) một cách... đồng bộ (synchronous) hơn.
Một promise sẽ có một trong 3 trạng thái:
- pending: mới khởi tạo - chờ xử lý
- fulfilled: thành công hoàn thành thao tác xử lý
- rejected: thao tác xử lý thất bại hay xảy ra lỗi
Trong promise object, ta có 2 properties: value
và onFulfilled
Do chúng ta không biết khi nào dữ liệu chúng ta cần sẽ có nên ta cần then
để xử lý khi dữ liệu được trả về cho chúng ta, dữ liệu được trả về từ web browser API/ feature sẽ được lưu trong value property.
Functions/codes nào được gắn vào promise object thông qua method then
, sẽ được tự động thực thi nếu như property value của promise object đó được cập nhật.
onFulfilled
là một array trống - ta có thể thêm vào đó bất kỳ functions nào, bất kỳ codes nào mà chúng ta muốn tự động kích hoạt với sự trợ giúp của JavaScript để chạy khi property value
được điền vào.
Nói lý thuyết vậy thôi, giờ ta đi vào ví dụ, ta có chương trình sau:
Và vì chúng ta đã quen thuộc với các khái niệm như call stack, thread of execution, execution context hay setTimeout hoạt động nên mình sẽ đi nhanh hơn.
Khi chương trình được chạy, nó sẽ thực thi, lần lượt như sau:
- Khai báo 3 function: display, printHello, blockFor300ms ở global memor
- Tại thời điểm
1ms
- thực thi dòng 12 -setTimeout(printHello,0)
Timer
được set, và lúc này tại thời điểm 1ms
thì trạng thái của Timer cũng đã hoàn thành, ta có đẩy printHello
vào call stack và thực thi không? Không! Tại sao? Nhớ nguyên tắc của event loop chứ?
Callback queue chỉ được nhìn tới khi và chỉ khi:
- Global execution context đã thực thi xong hết code ở global
- Call stack trống
Lúc này chưa thoả điều kiện để xem đến callback queue, nên ta sẽ trở lại global.
- Tại thời điểm
2ms
- thực thi dòng 15
Khai báo constant futureData
với method fetch
Method fetch
này vừa chơi với JavaScript, vừa chơi với Web Browser.
- Trong JavaScript: nó tạo một promise object với 2 properties:
value
vàonFulfilled
- Với Web Browser: nó set một network request
Tại thời điểm này, 2 properties của promise object đều đang trống, trạng thái của promise object là pending
. Với Web browser thì network request chưa hoàn thành ở thời điểm 2ms
, nên on completion
của chúng ta là trả về dữ liệu cho futureData.value
đang trống.
- Ở dòng 17 - ta thực thi
futureData.then(display);
Function display
được thêm vào onFulfilled
của promise object futureData
, ở global memory.
Ở phần event loop, ta biết callback function sẽ được thêm vào callback queue để chờ được thực thi, với promise - ta đưa deffered functions vào microtask queue
. Vì thế display được thêm vào microtask queue.
Ta lại trở về global
- Tiếp theo, tại thời điểm
3ms
- dòng 19 - functionblockFor300ms
được thực thi
Sau khi blockFor300ms
được thực thi xong, lúc này thời gian từ lúc thực thi đang là 303ms
.
Lưu ý, nãy giờ bộ đếm thời gian vẫn hoạt động, và lúc270ms
- ở network request, trạng thái đã trở thành hoàn thành - object promise futureData
lúc này đang ở trạng thái fulfilled và futureData.value
cũng đã có dữ liệu.
Như ở phần lý thuyết trên, chúng ta có nói:
Functions/codes nào được gắn vào promise object thông qua method then, sẽ được tự động thực thi nếu như property value của promise object đó được cập nhật.
Tại sao function
display vẫn chưa được thực thi?
Function display
đang nằm trong microtask queue. Cũng giống như call back queue, microtask queue chỉ được xem tới nếu như event loop xác nhận:
- Ở global - code đã được thực thi hết
- Ở call stack đang trống
Nếu chưa hiểu cách event loop hoạt động, bạn hãy xem lại bài này.
Vậy giữa microtask queue và callback queue thì event loop ưu tiên cái nào hơn? Ưu tiên microtask queue!
Chỉ khi nào microtask queue và call stack đang trống, code ở global đã thực thi xong hết thì event loop mới ghé đến callback queue và xem coi nó có gì trong đó đang đợi để được thực thi không.
Vì ở global vẫn còn code, nên display chưa được thực thi, ta trở về global.
- Tại thời điểm
303ms
- Thực thi dòng 21 - in raMe first!
- Lúc này event loop vẫn đang xem call stack có trống hay không, ở global đã thực thi xong hết code chưa. Và vì call stack đã trống, global đã hết code. Nó tiến đến kiểm tra microtask queue - thấy display.
Tại thời điểm 304ms
: display
được đẩy vào callstack và thực thi - in ra Hi
Trở về global vì event loop thấy microtask queue đã trống.
- Sau khi thấy microtask queue đã trống, event loop đi đến kiểm tra callback queue, và thấy
printHello
ở đây.
Tại thời điểm 305ms
:printHello
được đẩy vào callstack và thực thi - in ra Hello
Vậy là chương trình đã thực thi xong, chúng ta cũng đã tìm hiểu về Promise, microtask queue, event loop hoạt động như thế nào.
Bài này nằm trong chuỗi bài viết về JavaScript của em/ mình khi đang học, nếu có hiểu sai hay còn thiếu xót mong các bạn, anh chị góp ý!