Promise & microtask queue trong JavaScript

Promise & microtask queue trong JavaScript

·

5 min read

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: valueonFulfilled

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:

  1. Khai báo 3 function: display, printHello, blockFor300ms ở global memor

  1. 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.

  1. 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: valueonFulfilled

  • 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.

  1. 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.

Lưu ý: Khi dữ liệu quay trở lại từ api, chúng ta lưu trữ dữ liệu đó trong futureData.value, sau đó thêm function vào onfFulfilled vào microtask queue.

Ta lại trở về global

  1. Tiếp theo, tại thời điểm 3ms - dòng 19 - function blockFor300ms đượ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, display cũng đã được thêm vào microtask queue.

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.

  1. Tại thời điểm 303ms - Thực thi dòng 21 - in ra Me first!

  2. 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.

  1. 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 ý!