Que sont exactement les macrotâches et les microtâches ?

Commençons par une question d'entrevue courante :
console.log('start')

setTimeout(() => {
    
    
  console.log('setTimeout')
}, 0)

new Promise((resolve) => {
    
    
  console.log('promise')
  resolve()
})
  .then(() => {
    
    
    console.log('then1')
  })
  .then(() => {
    
    
    console.log('then2')
  })

console.log('end')

Beaucoup d'élèves devraient être capables d'y répondre, et le résultat est le suivant :

start 
promise
end
then1
then2
setTimeout

Cela implique des macro-tâches et des micro-tâches dans l'interrogation d'événements JavaScript. Alors, pouvez-vous préciser ce que sont exactement les macrotâches et les microtâches ? Qui l'a initié ? Pourquoi les microtâches s'exécutent avant les macrotâches ?

Tout d'abord, nous devons connaître le mécanisme de fonctionnement de JS.

Mécanisme de fonctionnement JS

Concept 1 : JS est une exécution monothread

"JS est monothread" fait référence au thread du moteur JS.
在浏览器环境中,有JS 引擎线程和渲染线程,且两个线程互斥。
Node环境中,只有JS 线程。

Notion 2 : Hôte

L'environnement dans lequel JS s'exécute. Généralement un navigateur ou Node.

Concept 3 : pile d'exécution

C'est une structure de pile pour stocker les appels de fonction, suivant le principe du premier entré, dernier sorti.

function foo() {
    
    
  throw new Error('error')
}
function bar() {
    
    
  foo()
}
bar()

insérez la description de l'image ici
Lors du démarrage de l'exécution du code JS, une fonction principale sera d'abord exécutée, puis notre code sera exécuté. Selon le principe du premier entré, dernier sorti, la fonction exécutée en dernier sera extraite de la pile en premier. Dans la figure, nous pouvons également constater que la fonction foo est exécutée en dernier, et elle sera extraite de la pile après exécution.

Concept 4 : boucle d'événements

Comment fonctionne JS ?
insérez la description de l'image ici

JS引擎常驻于内存中,等待宿主将JS代码或函数传递给它。
也就是等待宿主环境分配宏观任务,反复等待 - 执行即为事件循环。

Dans la boucle d'événements, chaque cycle est appelé un tick, et les tâches de chaque tick sont les suivantes :

  • La pile d'exécution sélectionne la tâche de macro (généralement un script) qui entre en premier dans la file d'attente et exécute son code de synchronisation jusqu'à la fin ;
  • Vérifiez s'il y a une microtâche, si c'est le cas, elle sera exécutée jusqu'à ce que la file d'attente des microtâches soit vide ;
  • Si l'hôte est un navigateur, la page peut être rendue ;
  • Démarrez la prochaine série de ticks et exécutez le code asynchrone dans la tâche de macro (rappels tels que setTimeout).
Concept 5 : Macrotâches et Microtâches

ES6 规范中,microtask 称为 jobs,macrotask 称为 task
宏任务是由宿主发起的,而微任务由JavaScript自身发起。

Dans ES3 et les versions précédentes, JavaScript lui-même n'a pas la capacité de lancer des requêtes asynchrones, il n'y a donc pas de microtâche. Après ES5, JavaScript a introduit Promise, afin que le moteur JavaScript lui-même puisse lancer des tâches asynchrones sans navigateur.

Auteur : Sister Na Chat Front
Lien : https://www.jianshu.com/p/bfc3e319a96b
Source : Jianshu
Le droit d'auteur appartient à l'auteur. Pour une réimpression commerciale, veuillez contacter l'auteur pour autorisation, pour une réimpression non commerciale, veuillez indiquer la source.

Donc, pour résumer, la différence entre les deux est :

Tâche macro (macrotask) microtâche
qui a initié Hôte (nœud, navigateur) Moteur JS
incident spécifique 1. script (peut être compris comme code de synchronisation externe) 2. setTimeout/setInterval 3. Rendu UI/événement UI 4. postMessage, MessageChannel 5. setImmediate, I/O (Node.js) 1. Promise 2. MutaionObserver 3. Object.observe (obsolète ; remplacement d'objet proxy) 4. process.nextTick (Node.js)
qui court le premier après la course courir en premier
Une nouvelle série de Tick sera-t-elle déclenchée ? réunion Ne le fera pas
Extension 1 : Comment fonctionne asyncet awaitgère les tâches asynchrones ?

En termes simples, asyncen Promiseenveloppant des tâches asynchrones.
Par exemple, il y a le code suivant :

async function async1() {
    
    
  await async2()
  console.log('async1 end')
}
async function async2() {
    
    
  console.log('async2 end')
}
async1()

Changé à la manière d'écrire ES5 :

new Promise((resolve, reject) => {
    
    
  // console.log('async2 end')
  async2() 
  ...
}).then(() => {
    
    
 // 执行async1()函数await之后的语句
  console.log('async1 end')
})

Lorsque async1la fonction , elle sera sortie immédiatement async2 end, et la fonction renvoie un Promise, puis lorsqu'elle awaitrencontre elle laisse le thread commencer à exécuter async1le code à l'extérieur (vous pouvez awaitle considérer comme un signe de lâcher le thread ).
Ensuite, lorsque tous les codes synchrones sont exécutés, tous les codes asynchrones seront exécutés, puis il reviendra awaità la position pour exécuter thenle rappel dans .

Extension 2 : setTimeout, setImmediatequi s'exécutera en premier ?

setImmediateet sont des méthodes couramment utilisées dans l'environnement (prises en charge par IE11 process.nextTick) , l'analyse ultérieure est donc basée sur l'hôte.NodesetImmediateNode

Node.js s'exécute côté serveur. Bien qu'il utilise le moteur V8, son API est quelque peu différente du JS natif en raison de différents objectifs de service et d'environnements. Sa boucle d'événements doit également gérer certaines E/S, telles que le nouveau Connexion réseau, etc., donc ce n'est pas la même chose que la boucle d'événement du navigateur.

La séquence d'exécution est la suivante :

  1. timers : rappels pour exécuter setTimeout et setInterval.
  2. rappels en attente : rappels d'E/S dont l'exécution est différée jusqu'à la prochaine itération de la boucle.
  3. inactif, préparer : uniquement utilisé en interne par le système.
  4. poll : récupère les nouveaux événements d'E/S ; exécute les rappels liés aux E/S. En fait, à l'exception des choses traitées dans plusieurs autres étapes, presque tous les autres processus asynchrones sont traités dans cette étape.
  5. check : setImmediate est exécuté ici.
  6. close callbacks : certaines fonctions de rappel de fermeture, telles que : socket.on('close', …).

De manière générale, setImmediateil sera setTimeoutexécuté avant, comme suit :

console.log('outer');
setTimeout(() => {
    
    
  setTimeout(() => {
    
    
    console.log('setTimeout');
  }, 0);
  setImmediate(() => {
    
    
    console.log('setImmediate');
  });
}, 0);

Sa séquence d'exécution est :

  1. La couche externe est un setTimeout, donc quand son rappel est exécuté, il est déjà dans l'étape des temporisateurs
  2. Traitez le setTimeout à l'intérieur, car les temporisateurs de ce cycle sont en cours d'exécution, de sorte que son rappel est en fait ajouté à la prochaine étape des temporisateurs
  3. Traiter le setImmediate à l'intérieur et ajouter son rappel à la file d'attente de la phase de vérification
  4. Après l'exécution de la phase des temporisateurs externes, entrez les rappels en attente, inactif, préparez, interrogez, ces files d'attente sont vides, continuez donc vers le bas
  5. Au stade de la vérification, j'ai trouvé le rappel de setImmediate et je l'ai sorti pour exécuter
  6. Fermez ensuite les rappels, la file d'attente est vide, sautez
  7. C'est à nouveau l'étape des minuteries, exécutez console.log('setTimeout')

Cependant, si l'environnement d'exécution actuel n'est pas l'étape des temporisateurs, ce n'est pas nécessairement le cas. . . . Au passage, vulgarisons setTimeoutle traitement spécial rencontré dans Node : setTimeout(fn, 0)il va être obligé de changer setTimeout(fn, 1).

Jetez un oeil à l'exemple ci-dessous:

setTimeout(() => {
    
    
  console.log('setTimeout');
}, 0);

setImmediate(() => {
    
    
  console.log('setImmediate');
});

Sa séquence d'exécution est :

  1. Lors de la rencontre de setTimeout, bien qu'il soit configuré pour se déclencher à 0 milliseconde, il est forcé d'être changé en 1 milliseconde par node.js et inséré dans l'étape times
  2. Rencontré setImmediate bourré dans l'étape de vérification
  3. Une fois le code synchrone exécuté, entrez dans la boucle d'événements
  4. Entrez d'abord l'étape times, vérifiez si l'heure actuelle a dépassé 1 milliseconde, si 1 milliseconde s'est écoulée, la condition setTimeout est remplie et le rappel est exécuté, si 1 milliseconde ne s'est pas écoulée, sautez
  5. Ignorez l'étape vide, entrez dans l'étape de vérification et exécutez le rappel setImmediate

On peut voir que 1 milliseconde est un point clé, donc dans l'exemple ci-dessus, setImmediateil ne peut pas setTimeoutêtre exécuté avant.

Extension 3 : Qui exécutera Promise et traitera.nextTick en premier ?

Comme process.nextTickil s'agit d'une méthode dans l'environnement Node, l'analyse ultérieure est toujours basée sur Node.
process.nextTick()est une API asynchrone spéciale qui n'appartient à aucune Event Loopphase. En fait, lorsque Node rencontre cette API, la boucle d'événements ne continuera pas du tout, elle cessera de s'exécuter immédiatement process.nextTick()et continuera après l'exécution Event Loop.

Par conséquent, lorsque nextTicket Promiseapparaissent en même temps, il doit être nextTickexécuté en premier, car nextTickla file d'attente Promisea une priorité plus élevée que la file d'attente.

Extension 4 : Scénarios d'application - dans Vuevm.$nextTick

vm.$nextTickAccepte une fonction de rappel comme paramètre, qui est utilisé pour retarder l'exécution du rappel jusqu'au prochain cycle de mise à jour DOM.

Cette API est implémentée sur la base de la boucle d'événements.
"Prochain cycle de mise à jour du DOM" signifie mettre à jour le DOM lors de la prochaine exécution de la microtâche, et vm.$nextTick consiste à ajouter la fonction de rappel à la microtâche (elle sera rétrogradée en macrotâche dans des cas particuliers).

Parce que la priorité des microtâches est trop élevée, après la version 2.4 de Vue, une méthode pour forcer l'utilisation des macrotâches est fournie.

  • vm.$nextTick utilise d'abord Promise pour créer des microtâches.
  • Si Promise n'est pas pris en charge ou si les tâches de macro sont forcées d'être activées, les tâches de macro seront lancées dans l'ordre suivant :
  1. Donner la priorité à la détection du support natif setImmediate (il s'agit d'une fonctionnalité uniquement prise en charge par les versions supérieures d'IE et Edge)
  2. S'il n'est pas pris en charge, vérifiez si le MessageChannel natif est pris en charge
  3. S'il n'est pas pris en charge, il sera rétrogradé à setTimeout.

résumé

Ce qui suit est une version améliorée des questions d'examen, vous pouvez l'essayer.

console.log('script start')

async function async1() {
    
    
  await async2()
  console.log('async1 end')
}
async function async2() {
    
    
  console.log('async2 end')
}
async1()

setTimeout(function() {
    
    
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
    
    
  console.log('Promise')
  resolve()
})
  .then(function() {
    
    
    console.log('promise1')
  })
  .then(function() {
    
    
    console.log('promise2')
  })

console.log('script end')
Ce n'est pas facile à faire, si ça aide, likez - suivez !

Je suppose que tu aimes

Origine blog.csdn.net/weixin_45449504/article/details/117419904
conseillé
Classement