+2

Promise & microtask queue trong JavaScript

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 property value. Property onFulfilled là một array trống - ta có thể thêm vào đó bất kỳ functions, bất kỳ codes nào mà chúng ta muốn tự động kích hoạt để thực thi với sự trợ giúp của JavaScript khi property value được điền vào.

Functions/codes nào được gắn vào property onFulfilled 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.

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

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 memory

  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: tạo một promise object với 2 properties: valueonFulfilled
  • Với Web Browser: 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 chưa được thực thi.

  1. Trở về global và thực thi dòng 17 - futureData.then(display)

Function display được thêm vào property onFulfilled của promise object futureData, ở global memory.

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úc 270ms - ở network request, trạng thái đã trở thành hoàn thành - promise object futureData lúc này đang ở trạng thái fulfilled và property value của futureData đã được cập nhật,

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 property onFulfilled của 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.

Lúc này, property value của futureData đã được cập nhật, function display cũng được thêm vào microtask queue rồi. Vậy tại sao function display vẫn chưa được thực thi?

Vì function display đang nằm trong microtask queue. Cũng giống 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: trống

Vậy thì giữa microtask queue và callback queue thì event loop ưu tiên đến queue nào? Đáp án là microtask queue!

Chỉ khi nào microtask queue và call stack đang trống, và code ở global đã thực thi xong hết thì event loop mới ghé đến callback queue và kiểm tra có gì trong đó đang đợi để được thực thi không.

Và tại vì ở global vẫn còn code, nên microtask queue chưa được xem tới, 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. Ai làm việc người ấy, event loop thì vẫn miệt mài xem call stack có trống hay không, ở global đã thực thi xong hết code chưa.

Và vì call stack đang trống, global đã hết code. Nó tiến đến kiểm tra microtask queue, tại đây event loop thấy function display.

Tại thời điểm 304ms: function display được lấy ra khỏi microtask queue và được đẩy vào callstack để thực thi - in ra Hi

  1. Sau khi thấy microtask queue đã trống, event loop đi đến kiểm tra callback queue, và thấy function printHello ở đây.

Tại thời điểm 305ms: function printHello được lấy ra khỏi callback queue và đượ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, và microtask queue 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 ý!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí