15 Desember 2021

Microtasks

Handler-handler promise .then/.catch/.finally selalu asynchronous.

Bahkan ketika sebuah promise diresolve, kode pada baris di bawah .then/.catch/.finally masih akan dieksekusi sebelum handler-handler ini.

Berikut demo-nya:

let promise = Promise.resolve();

promise.then(() => alert("promise done!"));

alert("code finished"); // alert ini muncul lebih dahulu

Jika anda menjalankannya, anda melihat code finished lebih dahulu, dan kemudian promise done!.

Itu aneh, karena promise pasti done dari awal.

Mengapa .then terpicu setelahnya? Apa yang sedang terjadi?

Antrean Microtasks

Task asynchronous membutuhkan manajemen yang tepat. Untuk itu, standar menentukan antrean internal PromiseJobs, lebih sering disebut sebagai “antrean microtask” (istilah v8).

Seperti yang dikatakan di spesifikasi:

  • Antrean adalah yang pertama masuk-pertama keluar: tasks yang diantrekan pertama dijalankan terlebih dahulu.
  • Eksekusi dari task dimulai jika hanya tidak ada yang berjalan.

Atau, untuk mengatakannya secara sederhana, ketika promise sudah siap, handler-handler .then/catch/finally ini dimasukkan kedalam antrean. Handler-handler tersebut belum dieksekusi. Mesin JavaScript mengambil tugas dari antrean dan menjalankannya.

Itulah kenapa “code finished” pada contoh di atas muncul lebih dahulu.

Handler-handler promise selalu melalui antrean internal.

Jika ada chain dengan banyak .then/catch/finally, maka masing-masing dieksekusi secara asynchronous. Artinya, itu pertama kali masuk ke antrean, dan dieksekusi ketika kode saat ini sudah selesai dan antrean handler-handler sebelumnya sudah selesai.

Bagaimana jika urutan penting untuk kita? Bagaimana kita membuat code finished berjalan setelah promise done?

Mudah, letakkan saja di dalam antrean dengan .then:

Promise.resolve()
  .then(() => alert("promise done!"))
  .then(() => alert("code finished"));

Sekarang urutannya seperti yang diinginkan.

Rejection yang tidak tertangani

Ingat event unhandledrejection dari bab Penanganan error dengan promise?

Sekarang kita bisa melihat bagaimana sebenarnya JavaScript menemukan bahwa ada rejection yang tidak tertangani.

“Rejection yang tidak tertangani” muncul ketika error promise tidak ditangani di akhir antrean microtask.

Biasanya, jika kita mengharapkan error, kita menambahkan .catch ke chain promise untuk menangani error tersebut:

let promise = Promise.reject(new Error("Promise Failed!"));
promise.catch(err => alert('caught'));

// tidak berjalan: error ditangani
window.addEventListener('unhandledrejection', event => alert(event.reason));

…Tetapi jika kita lupa menambah .catch, kemudian, setelah antrean microtask sudah kosong, mesin memicu event: