[JavaScript] setTimeout, Promise, Async / ordre d'exécution Await (fine)


Note: Cet environnement d'exécution de l'article pour la version actuelle de Google Chrome (72.0.3626.109)

Récemment, nous avons vu les questions face avant sur une telle boucle d'événement:

//请写出输出内容
async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
	console.log('async2');
}

console.log('script start');

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

async1();

new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');

sortie:

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

Cette question est essentiellement de l'ordre d'exécution de la fonction de boucle d'événements, y compris async, await, setTimeout, fonction Promise. Voici ce que cette question impliquée dans les points de connaissances.

File d'attente concept de tâche

Tout d'abord, nous devons comprendre les choses suivantes:

  • JS divisé en tâches synchrones et asynchrones Tâches
  • les tâches de synchronisation sont exécutées sur le fil conducteur pour former une exécution de la pile
  • En dehors du thread principal, l'événement déclenché thread gère une file d'attente de tâches, tant que les tâches asynchrones en cours d'exécution avec les résultats, il est placé dans un événement de file d'attente d'emploi
  • Une fois que tous l'exécution simultanée des tâches accomplies pile (ralenti du moteur JS à ce moment), le système lit la file d'attente des tâches sera ajouté aux tâches asynchrones en cours d'exécution pile exécutable, a commencé

Selon la spécification, la boucle d'événements doit être coordonnée par le mécanisme de la file d' attente de tâches. Event boucle a, il peut y avoir une ou plusieurs tâches de files d' attente (file d'attente de tâches), une tâche de la file d' attente est une collection de tâches ordonnées (tâche), et chaque tâche a une tâche de source (source de la tâche), dérivée de la même tâche une source de tâche doit être placé dans la même file d' attente de tâches, provenir de différentes sources ont été ajoutées à différentes files d' attente. setTimeout / Promise et d' autres API est la source de la tâche, dans la file d' attente des tâches est leur tâches spécifiques assignées

Insérer ici l'image Description

cycle de l'événement (boucle de l'événement)

Le concept:
cycle principal de constante de fil JS de lecture de la tâche de la file d' attente des tâches, effectuer des tâches, qui gère le mécanisme de boucle d'événement appelé (boucle d'événements).

Remarque:
Chaque boucle d'événements a une file d'attente Microtask
pour chaque boucle d'événements ont une ou plusieurs files d' attente macrotaks (également appelée file d' attente des tâches)
une tâche de tâche peut être mise en file d' attente macrotask Microtask file d' attente dans
chaque boucle d'événements il sera effectué la première file d'attente de Microtask, après l'exécution est terminée, extraire une tâche file d' attente macrotask à se joindre à la file d'attente Microtask, puis continuer Microtask file d' attente, afin d'effectuer toutes les tâches se poursuivent jusqu'à la fin de l' exécution.

tâche macro (macrotasks)

Le concept:
(Macro) Tâche (également connu sous le nom tâche macro), il est entendu que chaque exécution de la pile de code est une macro pour effectuer des tâches (y compris chacun obtenir une pile d'exécution de rappel et d' événements dans l' exécution de la file d' attente d'événements)

Processus:
Navigateur Pour pouvoir faire la tâche interne JS (macro) et les tâches DOM à exécuter de manière séquentielle, sera à la fin d'une (macro) l' exécution des tâches, la tâche suivante (macro) pour effectuer avant le début du processus de re-rendu Page comme suit:

(Macro) tâche -> 渲染 -> (macro) tâche -> ...

API:
(Macro) Tâche comprend principalement: script (code entier), setTimeout, setInterval, E / S, l' interface utilisateur des événements d'interaction, postMessage, MessageChannel, setImmediate (environnement Node.js)

  • macrotasks
    setTimeout
    setImmerdiate
    setInterval
    I / O
    UI渲染

Micro-tâche (microtâches)

Le concept:
Microtask (également connu sous le nom de micro-tâche), il est entendu que les tâches effectuées immédiatement après la fin de l'exécution de la tâche en cours. C'est, avant la tâche en cours après la tâche, la tâche suivante, avant de rendre.

Processus:
Donc , sa vitesse de réponse par rapport à setTimeout (setTimeout est une tâche) sera plus rapide, parce que sans attendre le rendu. C'est, dans un certain macrotask exécuté, tous Microtask sera généré au cours de son exécution ont été achevées (avant rendu)

(Macro) tâche -> (micro) tâche -> 渲染 -> (macro) tâche -> ...

API:
Microtask comprend principalement: Promise.then, MutaionObserver, process.nextTick (environnement Node.js)

  • microtâches
    process.nextTick
    promesse
    Object.observe (废弃)
    MutationObserver

mécanisme d'exploitation

Dans la boucle d'événements, une fois pour chaque cycle de fonctionnement est appelé tic, tic chaque modèle de traitement des tâches est plus complexe, mais les principales étapes sont les suivantes:

  • Exécuter une tâche macro (non seulement obtenir de la pile de file d'attente d'événements)
  • Si vous rencontrez lors de l'exécution des micro-tâches, il sera ajouté à la file d'attente micro-tâche
  • Une fois la tâche terminée macro, micro application immédiate de toutes les tâches des micro-tâches en cours dans la file d'attente (exécution de la commande)
  • tâche macro en cours est terminée, commencer à vérifier render, puis rendre le fil de l'interface graphique pour prendre en charge
  • Après le rendu est terminé, le fil JS continue de prendre en charge, au début d'une tâche macro (prise de la file d'attente d'événements)

Flowchart est la suivante:
Insérer ici l'image Description

Promise of async et exécution immédiate

Nous savons que dans la Promesse Asynchronous la prise incarnent et, donc écrire dans le code Promise est exécuté comme tâche immédiatement synchronisé. Dans async / Attendent, avant l'émergence apparaît avec le Attendre les code est exécuté immédiatement. Ensuite, il a été Q'attendez quand il est arrivé?

Attendent ce qui a été fait

Du point de vue attendre le sens littéral est d'attendre, attente await est une expression, l'expression de la valeur de retour peut être un objet de promesse peut également être d'autres valeurs.

Beaucoup de gens pensent que, après l'expression await attendra après l'exécution continuera derrière le code est en fait un signe d'attendre pour laisser un fil. Attendent cette dernière expression procédera d'abord à nouveau, attendra derrière le code dans Microtask, puis va sauter hors de toute fonction async pour exécuter le code derrière

revue:

Parce que async attend lui - même promesse + du générateur de sucre syntaxique . Par conséquent attendre le code derrière Microtask. Une telle modification peut être présent pour le premier titre en question sur:

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

équivalent à

async function async1() {
	console.log('async1 start');
	Promise.resolve(async2()).then(() => {
                console.log('async1 end');
        })
}

L'analyse de cette question

Au-dessus de toutes les connaissances pertinentes sur les questions liées à cela, nous allons revenir à cette étape de question look étape comment les enfants.

  1. Tout d'abord, la boucle d'événements de la file d'attente tâche macro (macrotask) a commencé, cette fois, la mission file d'attente des tâches macro, un seul scénario (code entier), face à la source de la tâche (source de tâche), remettra la tâche à la tâche correspondante file d'attente. Par conséquent, la première étape dans l'exemple ci-dessus est réalisée comme indiqué ci-dessous:
    Insérer ici l'image Description

  2. Ensuite, nous avons vu d'abord définir les deux fonctions async, puis regardez vers le bas, puis des déclarations de la console rencontrés, début du script de sortie directe. Après la sortie, la tâche de script pour continuer vers le bas, setTimeout rencontré, comme source de macro tâche, il distribuera sa première mission à la file d'attente correspondante:
    Insérer ici l'image Description

  3. tâche de script à poursuivre dans la mise en œuvre de la fonction async1 (), ledit code de fonction async antérieure est immédiatement avant l'exécution await, il sera immédiatement sortie début de async1.

    Lorsque await lues , il attendra l'expression qui a effectué à nouveau, donc nous avons immédiatement sortie async2, puis await code plus tard est console.log('async1 end')ajouté à la file d' attente Microtask Promise, puis de nouveau sur le code de la fonction async1 à exécuter:
    Insérer ici l'image Description

  4. Continuer sur la tâche de script exécution, rencontré par exemple Promise. Promesse Puisque la fonction est exécutée immédiatement, et la .alors suivante sera distribuée à la file d'attente Microtask Promise. Vous devrez d'abord promise1 de sortie, puis exécutez la détermination, affecté à la promise2 de file d'attente correspondante:
    Insérer ici l'image Description

  5. tâche de script de continuer vers le bas, et enfin une seule sortie de la fin du script, jusqu'à présent, la tâche globale est terminée.

    Selon ce qui précède, après chaque exécution d'une tâche terminée macro sera de vérifier s'il y a microtâches, le cas échéant, jusqu'à ce que l'exécution microtâches vider Microtask file d'attente.

    Ainsi, la tâche après le script est terminé, commencer à chercher pour vider la file d'attente des tâches micro. A cette époque, la micro-tâche, la promesse de deux files d'attente des tâches async1 fin et promise2, selon ainsi à la sortie de fin de commande de async1, promise2. Lorsque tous microtâches terminé, il représente le premier tour du cycle est terminé.

  6. Le second tour est toujours le début du cycle de la file d'attente des tâches macro. À ce stade, la tâche macro dans une seule setTimeout, sortie directe peut être retirée, ce qui porte l'ensemble des processus prend fin.

Maintenant, je vais changer le code pour Impress.

Une variante de la formule

Dans une première variante de formule I sont également une fonction async2 devenir code de fonction Promise est la suivante:

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    //async2做出如下更改:
    new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
    });
}
console.log('script start');

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

new Promise(function(resolve) {
    console.log('promise3');
    resolve();
}).then(function() {
    console.log('promise4');
});

console.log('script end');

Vous pouvez d'abord voir par vous-même ce que la séquence de sortie serait, nous allons publication des résultats:

script start
async1 start
promise1
promise3
script end
promise2
async1 end
promise4
setTimeout

Après la première macrotask été exécuté, qui est, après la fin du script de sortie, ira à nettoyer tous Microtask. Il aura une promise2 de sortie, à la fin de async1, promise4, le reste dire non plus

Variant di (déformation)

Dans une deuxième variante, je attendrai plus tard dans async1 et async2 de code sont modifiés de manière asynchrone, comme suit:

async function async1() {
    console.log('async1 start');
    await async2();
    //更改如下:
    setTimeout(function() {
        console.log('setTimeout1')
    },0)
}
async function async2() {
    //更改如下:
	setTimeout(function() {
		alert('setTimeout2')
	},0)
}
console.log('script start');

setTimeout(function() {
    console.log('setTimeout3');
}, 100)
async1();

new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');

Structure de sortie:

script start
async1 start
promise1
script end
promise2
(执行setTimeout2对应的逻辑,此时为阻断式,只有用户交互确认后再执行下面的定时器)
setTimeout1
setTimeout3

Après la sortie est promise2, la prochaine sera séquentielle sortie dans l'ordre SetTimeOut se joindre à la file d' attente, le début du code que nous avons mis pour la file d' attente est setTimeout3 -> setTimeout2 -> setTimeout1, lorsque l'exécution est effectuée dans l' ordre, mais pas en attente d'une minuterie mis en œuvre dans prochaine minuterie d'exécution, cette fois pour voir qui est qui doit effectuer des sorties, ce temps peut être connu par le code setTimeout2 -> setTimeout1sont exécutés immédiatement, afin d'effectuer setTimeout2, mais la logique est ici déni d'Alert, donc seulement la fin de l'interaction, se poursuivra avec le reste des minuteries, la dernière fois sortie de façon séquentielle selon le calendriersetTimeout1 -> setTimeout3

variante trois

Troisième variante, je vois un visage neutralisé le titre original, dans l'ensemble très bien même, comme suit:

async function a1 () {
    console.log('a1 start')
    await a2()
    console.log('a1 end')
}
async function a2 () {
    console.log('a2')
}

console.log('script start')

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

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

a1()

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

promise2.then((res) => {
    console.log(res)
    Promise.resolve().then(() => {
        console.log('promise3')
    })
})
console.log('script end')

Rien de plus que de faire la tâche dans un micro morceau de l'article enfant point, si vous êtes en face du contenu lu les mots Cette question est certainement pas de problème, il peut également être modifié ici:

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

promise2.then((res) => {
    console.log(res)
    Promise.resolve().then(() => {
        console.log('promise3')
    })
})

équivalent à

new Promise((resolve) => {  
    console.log('promise2')
    resolve();
}).then(() => {
	Promise.resolve().then(() => {
        console.log('promise2.then')
    })
}).then(() => {
	Promise.resolve().then(() => {
        console.log('promise3')
    })
})

Les résultats sont les suivants:

script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout

Article de référence

Publié 134 articles originaux · a gagné les éloges 80 · vues 30000 +

Je suppose que tu aimes

Origine blog.csdn.net/Umbrella_Um/article/details/100698686
conseillé
Classement