Bir olay döngüsü bağlamında mikro görev ve makro görev arasındaki fark


140

Promises / A + belirtimini okumayı bitirdim ve mikro görev ve makro görev terimlerini tökezledim: bkz. Http://promisesaplus.com/#notes

Bu terimleri daha önce hiç duymadım ve şimdi farkın ne olabileceğini merak ediyorum?

Zaten web üzerinde bazı bilgiler bulmaya çalıştım, ancak bulduğum tek şey w3.org Arşivlerinden (bana farkı açıklamıyor) bu yazıdır: http://lists.w3.org/Archives /Public/public-nextweb/2013Jul/0018.html

Ayrıca, "macrotask" adlı bir npm modülü buldum: https://www.npmjs.org/package/macrotask Yine, farkın tam olarak ne olduğu açıklığa kavuşmuyor .

Tek bildiğim, bunun https://html.spec.whatwg.org/multipage/webappapis.html#task-queue ve https: //html.spec.whatwg'de açıklandığı gibi olay döngüsüyle ilgili bir şey olması. .org / multipage / webappapis.html # gerçekleştirmek-a-MicroTask-denetim noktası

Bu WHATWG spesifikasyonu göz önüne alındığında teorik olarak farklılıkları kendim elde edebilmem gerektiğini biliyorum. Ama eminim başkaları da bir uzman tarafından verilen kısa bir açıklamadan yararlanabilir.


Kısaca: birden çok iç içe olay sırası. Birini kendiniz bile uygulayabilirsiniz:while (task = todo.shift()) task();
Bergi

1
Biraz daha fazla ayrıntı isteyen biri için: JavaScript Ninja'nın Sırları, 2. Baskı, BÖLÜM 13 Hayatta kalan etkinlikler
Ethan

Yanıtlar:


220

Olay döngüsünün Tek etrafında go olacak tam olarak bir görev olarak işlendikten macrotask kuyruğu (bu kuyruk sadece denir görev sırası içinde WHATWG şartname ). Bu makro görev tamamlandıktan sonra, kullanılabilir tüm mikro görevler , yani aynı geçiş döngüsü içinde işlenir. Bu mikro görevler işlenirken, mikro görev kuyruğu tükenene kadar hepsi tek tek çalıştırılacak daha fazla mikro görev kuyruğa alabilirler.

Bunun pratik sonuçları nelerdir?

Bir mikro görev, diğer mikro görevleri tekrarlı olarak sıralarsa, bir sonraki makro görevin işlenmesi uzun sürebilir. Bu, engellenmiş bir kullanıcı arayüzü veya uygulamanızda bitmiş bir G / Ç rölantisiyle sonuçlanabileceğiniz anlamına gelir.

Bununla birlikte, en azından Node.js'nin process.nextTick işlevi ( mikro görevleri sıralayan ) ile ilgili olarak, process.maxTickDepth aracılığıyla bu tür engellemeye karşı dahili bir koruma vardır. Bu değer varsayılan olarak 1000'e ayarlanmıştır, bu sınıra ulaşıldıktan sonra mikro görevlerin daha sonraki işlenmesini keserek bir sonraki makro görevinin işlenmesini sağlar)

Ne için ne zaman kullanılır?

Temel olarak, kullanım microtasks zaman uyumlu bir şekilde uyumsuz şeyler yapmanız gerektiğinde (yani söyleyebilirim zaman en yakın gelecekte bu (mikro) görevi gerçekleştirmek ). Aksi takdirde, makro görevlere sadık kalın .

Örnekler

macrotasks: setTimeout , setInterval , setImmediate , requestAnimationFrame , I / O , UI işleme
mikro görevleri: process.nextTick , Promises , queueMicrotask , MutationObserver


4
Olay döngüsünde bir mikro görev kontrol noktası olmasına rağmen, çoğu geliştiricinin mikro görevlerle karşılaşacağı yer bu değildir. Mikro görevler JS yığını boşaldığında işlenir. Bu, bir görev içinde veya olay döngüsünün oluşturma adımlarında birçok kez olabilir.
JaffaTheCake

2
process.maxTickDepthçok uzun zaman önce kaldırıldı: github.com/nodejs/node/blob/…
RidgeA

yeni bir mikro görev eklemek için queueMicrotask () yöntemini de kullanabilirsiniz
ZoomAll

Teşekkürler @ZoomAll, şimdiye kadar queueMicrotask () bilmiyordum.
Cevaba

requestAnimationFrame (rAF) yalnızca mikro görevler oluşturmakla kalmaz. Genel olarak, rAF çağrısı ayrı bir kuyruk
ZoomAll

67

Spesifikasyondaki temel kavramlar :

  • Bir olay döngüsünde bir veya daha fazla görev kuyruğu vardır. (Görev kuyruğu makro görev kuyruğu)
  • Her olay döngüsünün bir mikro görev kuyruğu vardır.
  • görev kuyruğu = makro görev kuyruğu! = mikro görev kuyruğu
  • bir görev makro görev kuyruğuna veya mikro görev kuyruğuna aktarılabilir
  • bir görev kuyruğa (mikro / makro) aktarıldığında, iş hazırlamanın tamamlandığını kastediyoruz, böylece görev şimdi yürütülebilir.

Ve olay döngüsü işlem modeli aşağıdaki gibidir:

zaman çağrı yığını boştur, adımları-do

  1. görev kuyruklarındaki en eski görevi (görev A) seçin
  2. A görevi boşsa (görev kuyruklarının boş olduğu anlamına gelir), 6. adıma atlayın
  3. "şu anda çalışan görevi" "A görevine" ayarla
  4. "Görev A" çalıştır (geri arama işlevini çalıştır anlamına gelir)
  5. "şu anda çalışan görevi" null olarak ayarlayın, "görev A" yı kaldırın
  6. mikro görev kuyruğu gerçekleştir
    • (a). mikro görev kuyruğundaki en eski görevi (görev x) seçin
    • (b). görev x boşsa (mikro görev kuyrukları boş demektir), (g) adımına atlayın
    • (c). "şu anda çalışan görevi" "x görevine" ayarla
    • (d) .run "görev x"
    • (e) .set "şu anda çalışan görev" null olarak ayarlayın, "görev x" kaldırın
    • (f). mikro görev kuyruğundaki bir sonraki en eski görevi seçin, (b) adımına geçin
    • (g). mikro görev sırasını bitir;
  7. 1. adıma atlayın.

basitleştirilmiş bir süreç modeli aşağıdaki gibidir:

  1. makro görev kuyruğundaki en eski görevi çalıştırdıktan sonra kaldırın.
  2. tüm görevleri mikro görev kuyruğunda çalıştırdıktan sonra kaldırın.
  3. sonraki tur: makro görev kuyruğunda sonraki görevi çalıştır (2. adıma atla)

Hatırlamak için bir şey:

  1. bir görev (makro görev kuyruğunda) çalışırken, yeni olaylar kaydedilebilir.Bu nedenle yeni görevler oluşturulabilir.
    • promiseA.then () 'in geri araması bir görevdir
      • promiseA çözümlendi / reddedildi: görev, olay döngüsünün geçerli turunda mikro görev kuyruğuna aktarılacak.
      • promiseA beklemede: görev, olay döngüsünün gelecekteki turunda mikro görev kuyruğuna aktarılacak (bir sonraki tur olabilir)
    • setTimeout (geri arama, n) 's geri arama bir görevdir ve makro görev kuyruğuna aktarılır, hatta n 0'dır;
  2. mikro görev kuyruğundaki görev geçerli turda çalıştırılırken, makro görev kuyruğundaki görev olay döngüsünün bir sonraki turunu beklemek zorundadır.
  3. hepimiz biliyoruz ki "click", "scroll", "ajax", "setTimeout" ... geri çağırma görevlerdir, ancak js kodlarını komut dosyası etiketinde bir bütün olarak da (bir makrotask) hatırlamalıyız.

2
Bu harika bir açıklama! Paylaşım için teşekkürler!. Bahsetmek gereken bir şey daha NodeJ'lerde , setImmediate()makro / görev ve process.nextTick()bir mikro / iş.
LeOn - Han Li

6
Tarayıcı paintgörevleri ne olacak ? Hangi kategoriye sığarlar?
Efsaneler

Ben mikro görevler (gibi requestAnimationFrame) uygun olacağını düşünüyorum
Divyanshu Maithani

İşte v8 olay döngüsünün çalıştığı sıra -> Çağrı Yığını || Mikro Görevler || Görev Sırası || rAF || Ağaç Oluştur || Düzen || Boya || <OS Ekranda pikselleri çizmek için yerel çağrılar> <----- 1) DOM (yeni değişiklikler), CSSOM (yeni değişiklikler), render ağacı, düzen ve boya olay döngüsü zamanlayıcılarına göre requestAnimationFrame geri aramasından sonra gerçekleşir. Bu nedenle rAF'dan önce DOM işlemlerinizi bitirebilmeniz önemlidir, geri kalanı rAF'a girebilir. Not: rAF çağrıldığında bir makro-görev yürütmesi tetiklenir.
Anvesh Checka

Yanlış olup olmadığımı bilmiyorum ama bu yanıta katılmıyorum, mikro görevler makro görevden önce çalışıyor. codepen.io/walox/pen/yLYjNRq ?
Walox

9

Olay döngüsünü yığından ayrı olarak tartışamayız, bu yüzden:

JS'de üç "yığın" vardır:

  • tüm senkron çağrılar için standart yığın (bir fonksiyon diğerini çağırır, vb.)
  • daha yüksek önceliğe sahip tüm zaman uyumsuz işlemler için mikro görev kuyruğu (veya iş kuyruğu veya mikro görev yığını) (process.nextTick, Promises, Object.observe, MutationObserver)
  • daha düşük önceliğe sahip tüm zaman uyumsuz işlemler için makro görev kuyruğu (veya olay kuyruğu, görev kuyruğu, makro görev kuyruğu) (setTimeout, setInterval, setImmediate, requestAnimationFrame, G / Ç, UI oluşturma)
|=======|
| macro |
| [...] |
|       |
|=======|
| micro |
| [...] |
|       |
|=======|
| stack |
| [...] |
|       |
|=======|

Ve olay döngüsü şu şekilde çalışır:

  • yığıntan aşağıdan yukarıya doğru her şeyi yürütün ve SADECE yığın boş olduğunda yukarıdaki sıralarda neler olup bittiğini kontrol edin
  • mikro yığını kontrol edin ve mikro görev kuyruğu boşalana veya herhangi bir yürütme gerekmeyene kadar bir mikro görev birbiri ardına yığın yardımı ile (gerekirse) orada yürütün ve SADECE makro yığınını kontrol edin
  • Makro yığınını kontrol edin ve orada her şeyi (gerekirse) yığın yardımıyla yürütün

Yığın boş değilse Mico yığını dokunulmaz. Mikro yığın boş değilse VEYA herhangi bir yürütme gerektirmiyorsa makro yığını dokunulmaz.

Özetle: mikro görev kuyruğu neredeyse makro görev kuyruğuyla aynıdır, ancak bu görevlerin (process.nextTick, Promises, Object.observe, MutationObserver) makro görevlerden daha yüksek önceliği vardır.

Mikro, makro gibidir ancak daha yüksek önceliğe sahiptir.

Burada her şeyi anlamak için "nihai" kod var.

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
*/

1
Bir sıraya yığın çağırmak tamamen kafa karıştırıcıdır.
Bergi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.