Maison > interface Web > js tutoriel > le corps du texte

Bien comprendre le mécanisme d'exécution de JavaScript

coldplay.xixi
Libérer: 2020-06-16 16:57:23
avant
1660 Les gens l'ont consulté

Bien comprendre le mécanisme d'exécution de JavaScript

Le but de cet article est de garantir que vous comprenez parfaitement le mécanisme d'exécution de javascript. Si vous ne comprenez toujours pas après avoir lu cet article, vous pouvez me battre.

Que vous soyez novice en JavaScript ou une personne expérimentée, que vous passiez un entretien pour un emploi ou que vous effectuiez un travail de développement quotidien, nous rencontrons souvent des situations comme celle-ci : étant donné quelques lignes de code, nous avons besoin de connaître son résultat. contenu et ordre. Parce que javascript est un langage monothread, nous pouvons tirer la conclusion :

  • javascript est exécuté dans l'ordre dans lequel les instructions apparaissent

Les lecteurs seront battus en voyant cela Lao : Est-ce que je ne sais pas que js est exécuté ligne par ligne ? As-tu vraiment besoin de dire ça ? Soyez patient, car js est exécuté ligne par ligne, nous pensons donc que js est comme ceci :

let a = '1';console.log(a);let b = '2';console.log(b);复制代码
Copier après la connexion

Cependant, en fait, js est comme ceci :

setTimeout(function(){    console.log('定时器开始啦')
});new Promise(function(resolve){    console.log('马上执行for循环啦');    for(var i = 0; i < 10000; i++){
        i == 99 && resolve();
    }
}).then(function(){    console.log(&#39;执行then函数啦&#39;)
});console.log(&#39;代码执行结束&#39;);复制代码
Copier après la connexion

Selon le concept selon lequel js est exécuté dans l'ordre dans lequel les instructions apparaissent , j'ai écrit en toute confiance le résultat résultats :

//"定时器开始啦"//"马上执行for循环啦"//"执行then函数啦"//"代码执行结束"复制代码
Copier après la connexion

Je suis allé sur Chrome pour le vérifier, et le résultat était complètement faux pendant un moment. Et si vous l'exécutiez ligne par ligne comme promis ?

Nous devons vraiment bien comprendre le mécanisme d'exécution de javascript.

1. À propos de javascript

javascript est un langage monothread est proposé dans le dernier HTML5, mais le cœur de javascript est qu'il est unique. -threadé. Par conséquent, toutes les versions JavaScript du « multi-threading » sont simulées avec un seul thread, et tous les multi-threads JavaScript sont des tigres de papier !

2.boucle d'événements javascript

Étant donné que js est monothread, c'est comme une banque avec une seule fenêtre, les clients doivent faire la queue pour gérer les affaires une par une. doivent être exécutés un par un. Si une tâche prend trop de temps, la tâche suivante devra également attendre. Alors voici la question : si nous voulons parcourir les actualités, mais que les images ultra haute définition contenues dans les actualités se chargent très lentement, notre page Web doit-elle rester bloquée jusqu'à ce que les images soient entièrement affichées ? Par conséquent, les programmeurs intelligents divisent les tâches en deux catégories :

  • Tâches synchrones
  • Tâches asynchrones

Lorsque nous ouvrons le site Web, le processus de rendu du Web page Il s'agit de nombreuses tâches de synchronisation, telles que le rendu du squelette de la page et des éléments de la page. Les tâches qui consomment beaucoup de ressources et prennent beaucoup de temps, comme le chargement d'images et de musique, sont des tâches asynchrones. Il existe une définition textuelle stricte de cette partie, mais le but de cet article est de bien comprendre le mécanisme d'exécution avec un coût d'apprentissage minimum, nous utilisons donc une carte pour illustrer :

Si le contenu à exprimer dans la carte est exprimé en mots :

  • Les tâches synchrones et asynchrones entrent respectivement dans différents "lieux" d'exécution, entrent de manière synchrone dans le thread principal, de manière asynchrone entrez dans la fonction Table d'événements et registre.
  • Lorsque l'opération spécifiée est terminée, la table des événements déplacera cette fonction dans la file d'attente des événements.
  • La tâche dans le thread principal est vide après l'exécution. Elle ira dans la file d'attente des événements pour lire la fonction correspondante et entrera dans le thread principal pour exécution.
  • Le processus ci-dessus sera répété en continu, ce qui est souvent appelé Event Loop.

Nous ne pouvons pas nous empêcher de demander : comment savons-nous que la pile d'exécution du thread principal est vide ? Il existe un processus de surveillance dans le moteur js, qui vérifiera en permanence si la pile d'exécution du thread principal est vide, une fois vide, elle ira dans la file d'attente des événements pour vérifier s'il y a une fonction en attente d'appel.

Après avoir dit tant de mots, il est préférable d'écrire un morceau de code pour être plus simple :

let data = [];
$.ajax({    url:www.javascript.com,    data:data,    success:() => {        console.log('发送成功!');
    }
})console.log('代码执行结束');复制代码
Copier après la connexion

Ce qui précède est un simple ajax code de demande :

  • ajax entre dans la table des événements, enregistrez la fonction de rappel success.
  • Exécuterconsole.log('代码执行结束').
  • L'événement ajax est terminé et la fonction de rappel success entre dans la file d'attente des événements.
  • Le thread principal lit la fonction de rappel success dans la file d'attente des événements et l'exécute.

Je crois que grâce au texte et au code ci-dessus, vous avez déjà une compréhension préliminaire de la séquence d'exécution de js. Étudions ensuite le sujet avancé : setTimeout.

3. Le setTimeout amour-haine

Le fameux setTimeout Inutile de dire que la première impression de chacun est que l'exécution asynchrone peut être retardée. C'est ainsi que nous implémentons souvent des retards. en 3 secondes :

setTimeout(() => {    console.log('延时3秒');
},3000)复制代码
Copier après la connexion

Petit à petit, setTimeout est utilisé à plus d'endroits, et des problèmes surviennent aussi Parfois il est écrit que le délai est de 3 secondes, mais il faut en réalité 5 ou 6 secondes pour exécuter la fonction. .Et alors ?

先看一个例子:

setTimeout(() => {
    task();
},3000)console.log('执行console');复制代码
Copier après la connexion

根据前面我们的结论,setTimeout是异步的,应该先执行console.log这个同步任务,所以我们的结论是:

//执行console//task()复制代码
Copier après la connexion

去验证一下,结果正确!
然后我们修改一下前面的代码:

setTimeout(() => {
    task()
},3000)

sleep(10000000)复制代码
Copier après la connexion

乍一看其实差不多嘛,但我们把这段代码在chrome执行一下,却发现控制台执行task()需要的时间远远超过3秒,说好的延时三秒,为啥现在需要这么长时间啊?

这时候我们需要重新理解setTimeout的定义。我们先说上述代码是怎么执行的:

  • task()进入Event Table并注册,计时开始。
  • 执行sleep函数,很慢,非常慢,计时仍在继续。
  • 3秒到了,计时事件timeout完成,task()进入Event Queue,但是sleep也太慢了吧,还没执行完,只好等着。
  • sleep终于执行完了,task()终于从Event Queue进入了主线程执行。

上述的流程走完,我们知道setTimeout这个函数,是经过指定时间后,把要执行的任务(本例中为task())加入到Event Queue中,又因为是单线程任务要一个一个执行,如果前面的任务需要的时间太久,那么只能等着,导致真正的延迟时间远远大于3秒。

我们还经常遇到setTimeout(fn,0)这样的代码,0秒后执行又是什么意思呢?是不是可以立即执行呢?

答案是不会的,setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。举例说明:

//代码1console.log('先执行这里');
setTimeout(() => {    console.log('执行啦')
},0);复制代码
Copier après la connexion
//代码2console.log('先执行这里');
setTimeout(() => {    console.log('执行啦')
},3000);复制代码
Copier après la connexion

代码1的输出结果是:

//先执行这里//执行啦复制代码
Copier après la connexion

代码2的输出结果是:

//先执行这里// ... 3s later// 执行啦复制代码
Copier après la connexion

关于setTimeout要补充的是,即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。有兴趣的同学可以自行了解。

4.又恨又爱的setInterval

上面说完了setTimeout,当然不能错过它的孪生兄弟setInterval。他俩差不多,只不过后者是循环的执行。对于执行顺序来说,setInterval会每隔指定的时间将注册的函数置入Event Queue,如果前面的任务耗时太久,那么同样需要等待。

唯一需要注意的一点是,对于setInterval(fn,ms)来说,我们已经知道不是每过ms秒会执行一次fn,而是每过ms秒,会有fn进入Event Queue。一旦setInterval的回调函数fn执行时间超过了延迟时间ms,那么就完全看不出来有时间间隔了。这句话请读者仔细品味。

5.Promise与process.nextTick(callback)

传统的定时器我们已经研究过了,接着我们探究Promiseprocess.nextTick(callback)的表现。

Promise的定义和功能本文不再赘述,不了解的读者可以学习一下阮一峰老师的Promise。而process.nextTick(callback)类似node.js版的"setTimeout",在事件循环的下一次循环中调用 callback 回调函数。

我们进入正题,除了广义的同步任务和异步任务,我们对任务有更精细的定义:

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise,process.nextTick

不同类型的任务会进入对应的Event Queue,比如setTimeoutsetInterval会进入相同的Event Queue。

事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务。听起来有点绕,我们用文章最开始的一段代码说明:

setTimeout(function() {    console.log('setTimeout');
})new Promise(function(resolve) {    console.log('promise');
}).then(function() {    console.log('then');
})console.log('console');复制代码
Copier après la connexion
  • 这段代码作为宏任务,进入主线程。
  • 先遇到setTimeout,那么将其回调函数注册后分发到宏任务Event Queue。(注册过程与上同,下文不再描述)
  • 接下来遇到了Promisenew Promise立即执行,then函数分发到微任务Event Queue。
  • 遇到console.log(),立即执行。
  • 好啦,整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了then在微任务Event Queue里面,执行。
  • ok,第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event Queue中setTimeout对应的回调函数,立即执行。
  • 结束。

事件循环,宏任务,微任务的关系如图所示:

我们来分析一段较复杂的代码,看看你是否真的掌握了js的执行机制:

console.log('1');

setTimeout(function() {    console.log('2');
    process.nextTick(function() {        console.log('3');
    })    new Promise(function(resolve) {        console.log('4');
        resolve();
    }).then(function() {        console.log('5')
    })
})
process.nextTick(function() {    console.log('6');
})new Promise(function(resolve) {    console.log('7');
    resolve();
}).then(function() {    console.log('8')
})

setTimeout(function() {    console.log('9');
    process.nextTick(function() {        console.log('10');
    })    new Promise(function(resolve) {        console.log('11');
        resolve();
    }).then(function() {        console.log('12')
    })
})复制代码
Copier après la connexion

第一轮事件循环流程分析如下:

  • 整体script作为第一个宏任务进入主线程,遇到console.log,输出1。
  • 遇到setTimeout,其回调函数被分发到宏任务Event Queue中。我们暂且记为setTimeout1
  • 遇到process.nextTick(),其回调函数被分发到微任务Event Queue中。我们记为process1
  • 遇到Promisenew Promise直接执行,输出7。then被分发到微任务Event Queue中。我们记为then1
  • 又遇到了setTimeout,其回调函数被分发到宏任务Event Queue中,我们记为setTimeout2
宏任务Event Queue 微任务Event Queue
setTimeout1 process1
setTimeout2 then1
  • 上表是第一轮事件循环宏任务结束时各Event Queue的情况,此时已经输出了1和7。

  • 我们发现了process1then1两个微任务。

  • 执行process1,输出6。
  • 执行then1,输出8。

好了,第一轮事件循环正式结束,这一轮的结果是输出1,7,6,8。那么第二轮时间循环从setTimeout1宏任务开始:

  • 首先输出2。接下来遇到了process.nextTick(),同样将其分发到微任务Event Queue中,记为process2new Promise立即执行输出4,then也分发到微任务Event Queue中,记为then2
宏任务Event Queue 微任务Event Queue
setTimeout2 process2

then2
  • 第二轮事件循环宏任务结束,我们发现有process2then2两个微任务可以执行。
  • 输出3。
  • 输出5。
  • 第二轮事件循环结束,第二轮输出2,4,3,5。
  • 第三轮事件循环开始,此时只剩setTimeout2了,执行。
  • 直接输出9。
  • process.nextTick()分发到微任务Event Queue中。记为process3
  • 直接执行new Promise,输出11。
  • then分发到微任务Event Queue中,记为then3
宏任务Event Queue 微任务Event Queue

process3

then3
  • Le troisième tour d'exécution de la macro-tâche de boucle d'événement se termine et deux micro-tâches process3 et then3 sont exécutées.
  • Sortie 10.
  • Sortie 12.
  • Le troisième tour de la boucle d'événements se termine et le troisième tour produit 9, 11, 10, 12.

Le code entier a un total de trois boucles d'événements et la sortie complète est 1, 7, 6, 8, 2, 4, 3, 5, 9, 11, 10, 12.
(Veuillez noter que la surveillance des événements dans l'environnement de nœud repose sur libuv et n'est pas exactement la même que l'environnement front-end, et l'ordre de sortie peut contenir des erreurs)

6.

(1)js Asynchronous

Nous avons dit depuis le début que JavaScript est un langage monothread. Quel que soit le nouveau framework ou le nouveau sucre de syntaxe qui implémente ce qu'on appelle asynchrone, il l'est. réellement simulé à l'aide de méthodes synchrones. Gardez-en une bonne compréhension.

(2) Boucle d'événement Boucle d'événement

La boucle d'événement est une méthode permettant à js d'atteindre l'asynchronisme et constitue également le mécanisme d'exécution de js.

(3) Il existe une grande différence entre l'exécution et l'exécution de javascript

Il existe une grande différence entre l'exécution et l'exécution de javascript Les méthodes d'exécution de javascript sont différentes selon les environnements. , tel que nœud, navigateur, Ringo, etc. Et l'exécution fait principalement référence au moteur d'analyse javascript, qui est unifié.

(4)setImmediate

Il existe de nombreux types de micro-tâches et de macro-tâches, telles que setImmediate et ainsi de suite. Les étudiants intéressés peuvent en apprendre davantage. tout seul.

(5) La dernière chose

  • javascript est un langage à thread unique
  • Event Loop est le mécanisme d'exécution de javascript

Saisissez fermement les deux points fondamentaux, concentrez-vous sur l'étude sérieuse de JavaScript et réalisez le grand rêve de devenir un maître du front-end le plus tôt possible !

Tutoriel recommandé : "Tutoriel sur les bases de JavaScript"

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:juejin.im
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal