Perbedaan antara mikrotask dan macrotask dalam konteks loop peristiwa

140

Saya baru saja selesai membaca spesifikasi Janji / A + dan menemukan istilah microtask dan macrotask: lihat http://promisesaplus.com/#notes

Saya belum pernah mendengar istilah ini sebelumnya, dan sekarang saya ingin tahu apa bedanya?

Saya sudah mencoba menemukan beberapa informasi di web, tetapi yang saya temukan hanyalah posting ini dari Arsip w3.org (yang tidak menjelaskan perbedaannya kepada saya): http://lists.w3.org/Archives /Public/public-nextweb/2013Jul/0018.html

Selain itu, saya telah menemukan modul npm yang disebut "macrotask": https://www.npmjs.org/package/macrotask Sekali lagi, tidak dijelaskan apa bedanya sebenarnya.

Yang saya tahu adalah, bahwa itu ada hubungannya dengan loop acara, seperti yang dijelaskan dalam https://html.spec.whatwg.org/multipage/webappapis.html#task-queue dan https: //html.spec.whatwg .org / multipage / webappapis.html # perform-a-microtask-checkpoint

Saya tahu saya secara teoritis harus dapat mengekstraksi perbedaan sendiri, mengingat spesifikasi WHATWG ini. Tapi saya yakin orang lain juga bisa mendapat manfaat dari penjelasan singkat yang diberikan oleh seorang ahli.

NicBright
sumber
Singkatnya: beberapa antrian acara bersarang. Anda bahkan dapat menerapkannya sendiri:while (task = todo.shift()) task();
Bergi
1
Untuk seseorang yang ingin sedikit lebih detail: Rahasia JavaScript Ninja, Edisi ke-2, BAB 13 Peristiwa yang bertahan
Ethan

Jawaban:

220

Satu putaran event loop akan memiliki satu tugas yang sedang diproses dari antrian macrotask (antrian ini hanya disebut antrian tugas dalam spesifikasi WHATWG ). Setelah macrotask ini selesai, semua microtasks yang tersedia akan diproses, yaitu dalam siklus go-around yang sama. Sementara mikrotasks ini diproses, mereka dapat mengantri lebih banyak mikrotasks, yang semuanya akan dijalankan satu per satu, hingga antrian mikrotask habis.

Apa konsekuensi praktis dari ini?

Jika mikrotask secara antri mengantri mikrotas lainnya, mungkin perlu waktu lama hingga macrotask berikutnya diproses. Ini berarti, Anda dapat berakhir dengan UI yang diblokir, atau pemalasan I / O yang selesai di aplikasi Anda.

Namun, setidaknya mengenai fungsi Node.js's process.nextTick (yang membuat antrian mikrotasks ), ada perlindungan bawaan terhadap pemblokiran semacam itu melalui proses.maxTickDepth. Nilai ini diatur ke default 1000, mengurangi pemrosesan mikrotasks lebih lanjut setelah batas ini tercapai yang memungkinkan macrotask berikutnya diproses)

Jadi kapan harus menggunakan apa?

Pada dasarnya, gunakan microtasks ketika Anda perlu melakukan hal-hal secara asinkron dengan cara yang sinkron (yaitu ketika Anda akan mengatakan melakukan tugas (mikro) ini di masa depan yang paling cepat ). Kalau tidak, tetap menggunakan macrotasks .

Contohnya

macrotasks: setTimeout , setInterval , setImmediate , requestAnimationFrame , I / O , UI rendering
microtasks: process.nextTick , Janji , antrianMicrotask , MutationObserver

NicBright
sumber
4
Meskipun ada pos pemeriksaan mikrotask di loop acara, ini bukan tempat sebagian besar pengembang akan menemukan mikrotask. Microtasks diproses ketika tumpukan JS kosong. Ini bisa terjadi berkali-kali dalam suatu tugas, atau bahkan dalam langkah render dari loop acara.
JaffaTheCake
2
process.maxTickDepthtelah dihapus sejak lama: github.com/nodejs/node/blob/…
RidgeA
Anda juga dapat menggunakan metode queueMicrotask () untuk menambahkan mikrotask baru
ZoomAll
Terima kasih @ZoomAll, tidak tahu queueMicrotask () sampai sekarang. Saya telah menambahkannya ke jawaban plus tautan ke semua hal ...
NicBright
requestAnimationFrame (rAF) tidak hanya menghasilkan microtasks. Secara umum panggilan rAF menciptakan antrian terpisah
ZoomAll
67

Konsep dasar dalam spesifikasi :

  • Event loop memiliki satu atau lebih tugas antrian. (Antrian tugas adalah antrian macrotask)
  • Setiap loop acara memiliki antrian mikrotask.
  • tugas antrian = antrian macrotask! = antrian mikrotask
  • suatu tugas dapat didorong ke antrian macrotask, atau antrian mikrotask
  • ketika suatu tugas didorong ke antrian (mikro / makro), kami maksudkan persiapan pekerjaan selesai, sehingga tugas tersebut dapat dieksekusi sekarang.

Dan model proses loop acara adalah sebagai berikut:

ketika tumpukan panggilan kosong, lakukan langkah-

  1. pilih tugas terlama (tugas A) dalam antrian tugas
  2. jika tugas A adalah nol (berarti antrian tugas kosong), lompat ke langkah 6
  3. setel "tugas yang sedang berjalan" ke "tugas A"
  4. jalankan "task A" (artinya jalankan fungsi callback)
  5. setel "tugas yang sedang berjalan" ke nol, hapus "tugas A"
  6. melakukan antrian mikrotask
    • (a). pilih tugas tertua (tugas x) dalam antrian mikrotask
    • (B) .Jika tugas x adalah nol (berarti antrian mikrotask kosong), lompat ke langkah (g)
    • (c) .set "tugas yang sedang berjalan" ke "tugas x"
    • (d). jalankan "tugas x"
    • (e). set "tugas yang sedang berjalan" ke nol, hapus "tugas x"
    • (f). pilih tugas tertua berikutnya dalam antrian mikrotask, lompat ke langkah (b)
    • (g). antrian mikrotask akhir;
  7. lompat ke langkah 1.

model proses yang disederhanakan adalah sebagai berikut:

  1. jalankan tugas terlama di antrian macrotask, lalu hapus.
  2. jalankan semua tugas yang tersedia dalam antrian mikrotask, lalu hapus.
  3. putaran berikutnya: jalankan tugas berikutnya dalam antrian macrotask (lompat langkah 2)

sesuatu untuk diingat:

  1. ketika tugas (dalam antrian macrotask) sedang berjalan, acara baru mungkin terdaftar. Jadi tugas baru dapat dibuat. Berikut adalah dua tugas baru yang dibuat:
    • callback janjiA.then () adalah tugas
      • janji A diselesaikan / ditolak: tugas akan didorong ke antrian mikrotask di putaran saat ini dari acara.
      • janji sedang menunggu: tugas akan didorong ke antrian mikrotask di putaran berikutnya dari acara loop (mungkin putaran berikutnya)
    • setTimeout (callback, n) callback adalah tugas, dan akan didorong ke antrian macrotask, bahkan n adalah 0;
  2. tugas dalam antrian mikrotask akan dijalankan di babak saat ini, sementara tugas dalam antrian macrotask harus menunggu putaran berikutnya dari perulangan acara.
  3. kita semua tahu panggilan balik dari "klik", "gulir", "ajax", "setTimeout" ... adalah tugas, namun kita juga harus ingat kode js karena keseluruhan dalam tag skrip adalah tugas (macrotask) juga.
wengeezhang
sumber
2
Ini penjelasan yang bagus! Terima kasih telah berbagi!. Satu lagi yang perlu disebutkan adalah di NodeJs , setImmediate()adalah makro / tugas, dan process.nextTick()merupakan mikro / pekerjaan.
LeOn - Han Li
6
Bagaimana dengan painttugas browser ? Dalam kategori apa mereka akan cocok?
Legenda
Saya pikir mereka akan cocok dalam tugas mikro (seperti requestAnimationFrame)
Divyanshu Maithani
Berikut adalah urutan pengulangan event v8 -> Call Stack || Tugas Mikro || Antrian Tugas || rAF || Render Tree || Tata Letak || Cat || <OS Panggilan asli untuk menggambar piksel pada layar> <----- 1) DOM (perubahan baru), CSSOM (perubahan baru), render tree, tata letak dan cat terjadi setelah requestAnimationFrame callback sesuai dengan pengatur waktu-loop acara. Inilah sebabnya mengapa penting untuk menyelesaikan operasi DOM Anda sebelum rAF sebanyak yang Anda bisa, sisanya bisa masuk dalam rAF. PS: memanggil rAF akan memicu eksekusi tugas makro.
Anvesh Checka
Saya tidak tahu apakah saya salah tetapi saya agak tidak setuju dengan jawaban ini, microtasks berjalan sebelum macrotask. codepen.io/walox/pen/yLYjNRq ?
walox
9

Saya pikir kita tidak bisa membahas event loop terpisah dari stack, jadi:

JS memiliki tiga "tumpukan":

  • tumpukan standar untuk semua panggilan sinkron (satu fungsi memanggil fungsi lain, dll)
  • microtask queue (atau job queue atau microtask stack) untuk semua operasi async dengan prioritas lebih tinggi (process.nextTick, Promises, Object.observe, MutationObserver)
  • antrian macrotask (atau antrian acara, antrian tugas, antrian macrotask) untuk semua operasi async dengan prioritas lebih rendah (setTimeout, setInterval, setImmediate, requestAnimationFrame, I / O, rendering UI)
|=======|
| macro |
| [...] |
|       |
|=======|
| micro |
| [...] |
|       |
|=======|
| stack |
| [...] |
|       |
|=======|

Dan loop acara berfungsi seperti ini:

  • jalankan semuanya dari bawah ke atas dari tumpukan, dan HANYA ketika tumpukan kosong, periksa apa yang terjadi dalam antrian di atas
  • periksa tumpukan mikro dan jalankan semua yang ada (jika diperlukan) dengan bantuan tumpukan, satu tugas mikro demi satu sampai antrian mikrotask kosong atau tidak memerlukan eksekusi apa pun dan HANYA kemudian periksa tumpukan makro
  • periksa tumpukan makro dan jalankan semua yang ada (jika perlu) dengan bantuan tumpukan

Mico stack tidak akan disentuh jika stack tidak kosong. Tumpukan makro tidak akan disentuh jika tumpukan mikro tidak kosong ATAU tidak memerlukan eksekusi apa pun.

Singkatnya: antrian mikrotask hampir sama dengan antrian macrotask tetapi tugas-tugas itu (process.nextTick, Promises, Object.observe, MutationObserver) memiliki prioritas lebih tinggi daripada macrotasks.

Mikro seperti makro tetapi dengan prioritas lebih tinggi.

Di sini Anda memiliki kode "pamungkas" untuk memahami segalanya.

console.log('stack [1]');
setTimeout(() => console.log("macro [2]"), 0);
setTimeout(() => console.log("macro [3]"), 1);

const p = Promise.resolve();
for(let i = 0; i < 3; i++) p.then(() => {
    setTimeout(() => {
        console.log('stack [4]')
        setTimeout(() => console.log("macro [5]"), 0);
        p.then(() => console.log('micro [6]'));
    }, 0);
    console.log("stack [7]");
});

console.log("macro [8]");

/* Result:
stack [1]
macro [8]

stack [7], stack [7], stack [7]

macro [2]
macro [3]

stack [4]
micro [6]
stack [4]
micro [6]
stack [4]
micro [6]

macro [5], macro [5], macro [5]
--------------------
but in node in versions < 11 (older versions) you will get something different


stack [1]
macro [8]

stack [7], stack [7], stack [7]

macro [2]
macro [3]

stack [4], stack [4], stack [4]
micro [6], micro [6], micro [6]

macro [5], macro [5], macro [5]

more info: https://blog.insiderattack.net/new-changes-to-timers-and-microtasks-from-node-v11-0-0-and-above-68d112743eb3
*/
pengguna1660210
sumber
1
Memanggil antrian tumpukan benar-benar membingungkan.
Bergi