Maison > Article > interface Web > Analyse de la séquence d'exécution JavaScript
Nous devons d'abord savoir que <code><span style="font-size: 14px;">JavaScript</span>
JavaScript est un langage Gateinterprété
à thread unique. Cela signifie que nous ne pouvons exécuter qu’une seule commande à la fois. La raison pour laquelle il s’agit d’un langage monothread est liée à son objectif.
JavaScript a été conçu à l'origine pour améliorer l'interaction entre le navigateur et l'utilisateur, en particulier l'interaction avec le formulaire. Plus tard, la technologie Ajax a également été inventée pour rendre l'interaction avec le formulaire plus humaine. Étant donné que JavaScript est un langage interprété et que l'interpréteur est intégré au navigateur, cet interpréteur est monothread. La raison pour laquelle il n'est pas conçu pour être multithread est que le multithread peut facilement provoquer des blocages ou des conflits de ressources lors du rendu des pages Web. Mais le navigateur lui-même est multithread. Par exemple, il interprète et exécute JavaScript lors du chargement des ressources réseau.
Pourquoi JavaScript ne prend-il pas en charge le multithreading ?
9918a60989ece9c0e9fb9608baca383f
Un seul thread signifie que si vous souhaitez exécuter beaucoup de commandes, ces commandes doivent être triées. Généralement, ces commandes sont exécutées dans l'ordre de haut en bas. vers le bas (car l'interpréteur commence en haut du fichier). Par exemple, le code suivant est exécuté dans l'ordre.
<span style="font-size: 14px;">console.log("1");<br>console.log("2");<br>console.log("3");<br>//1<br>//2<br>//3<br></span>
Mais on sait aussi qu'il existe une programmation asynchrone en JavaScript, comme Ajax, setTimeout, setInterval ou Promise, async, wait dans ES6.
L'exécution d'une commande sur l'ordinateur signifie qu'il utilise des ressources telles que le CPU à ce moment-là, il existe donc de nombreuses commandes qui souhaitent obtenir des ressources CPU, et le CPU exécute des commandes. Il faut également du temps pour calculer et obtenir le résultat, il y a donc la notion de synchrone et d'asynchrone.
La synchronisation signifie que lorsqu'une requête CPU est émise, la requête CPU ne reviendra pas tant que le résultat n'est pas obtenu. Mais une fois l’appel renvoyé, vous obtenez la valeur de retour.
Asynchrone signifie qu'une fois la requête CPU émise, l'appel revient directement, donc aucun résultat n'est renvoyé. Une fois l'opération terminée, une série de moyens sont nécessaires pour obtenir la valeur de retour
À ce stade, les notions de processus et de thread doivent être introduites.
Concept : Un processus est un processus avec certain Le processus d'exécution dynamique d'un programme avec des fonctions indépendantes sur un ensemble de données est une unité indépendante d'allocation et de planification des ressources par le système d'exploitation, et est le support de l'exécution des applications.
Étant donné que le processus utilise le processeur à tour de rôle, il y a une commutation de processus, mais en raison du programme actuel, ils sont tous relativement importants et la surcharge de commutation est très élevée, ce qui gaspillera les ressources du processeur. Les threads ont donc été inventés pour décomposer un processus volumineux en plusieurs threads pour une exécution conjointe.
Un processus est la plus petite unité permettant au système d'exploitation d'allouer des ressources, et un thread est la la plus petite unité pour l’exécution du programme.
Un processus se compose d'un ou plusieurs threads, qui sont différentes routes d'exécution de code dans un processus
Les processus sont indépendants les uns des autres, mais les threads d'un même processus partagent l'espace mémoire du programme (y compris les segments de code, les ensembles de données, les tas, etc.) et certaines ressources au niveau du processus (telles que les fichiers ouverts et les signaux ) ).
Planification et commutation : le changement de contexte de thread est beaucoup plus rapide que le changement de contexte de processus.
Si j'étais Naruto, je veux manger beaucoup de ramen, si j'en mange seul S'il y a 10 bols, alors je finirai de manger des ramen un processus et un fil.
Mais si j'utilise 9 clones pour manger 10 bols de ramen avec moi, alors je suis un processus utilisant 9 threads pour accomplir la tâche de manger des ramen. Et le multi-processus signifie que pendant que la célébrité mange des ramen à Ichiraku Ramen, l'immortel lubrique regarde la fille qui prend un bain~~. L'immortel lubrique est un processus unique et un fil unique pour jeter un coup d'œil !
Le noyau du navigateur est multi-thread Sous le contrôle du noyau, chaque thread coopère avec chacun. other pour maintenir la synchronisation. Les navigateurs se composent généralement des threads suivants :
thread de rendu GUI
JavaScript引擎线程
事件触发线程
异步http请求线程
EventLoop轮询的处理线程
这些线程的作用:
UI线程用于渲染页面
js线程用于执行js任务
浏览器事件触发线程用于控制交互,响应用户
http线程用于处理请求,ajax是委托给浏览器新开一个http线程
EventLoop处理线程用于轮询消息队列
因为JavaScript是单线程的,而浏览器是多线程的,所以为了执行不同的同步异步的代码,JavaScript运行的环境采用里事件循环和消息队列来达到目的。
每个线程的任务执行顺序都是FIFO(先进先出)
在JavaScript运行的环境中,有一个负责程序本身的运行,作为主线程;另一个负责主线程与其他线程的通信,被称为<span style="font-size: 14px;">Event Loop 线程</span>
。
每当主线程遇到异步的任务,把他们移入到<span style="font-size: 14px;">Event Loop 线程</span>
,然后主线程继续运行,等到主线程完全运行完之后,再去<span style="font-size: 14px;">Event Loop 线程</span>
拿结果。
而每个异步任务都包含着与它相关联的信息,比如运行状态,回调函数等。
由此我们可以知道,同步任务和异步任务会被分发到不同的线程去执行。
现在我们就可以分析一下一下代码的运行结果了。
<span style="font-size: 14px;">setTimeout(()=>{console.log("我才是第一");},0);<br>console.log("我是第一");<br></span>
因为setTimeout是异步的事件,所以主线程把它调入Event Loop线程进行注册。
主线程继续执行<span style="font-size: 14px;">console.log("我是第一");</span>
主线程执行完毕,从Event Loop 线程读取回调函数。再执行<span style="font-size: 14px;">console.log("我才是第一");</span>
;
这里值得一提的是,<span style="font-size: 14px;">setTimeout(callback,0)</span>
指的是主线程中的同步任务运行完了之后立刻由Event Loop 线程调入主线程。
而计时是在调入Event Loop线程注册时开始的,此时<span style="font-size: 14px;">setTimeout的回调函数执行时间</span>
与主线程运行结束的时间相关。
关于setTimeout要补充的是,即便主线程为空,0毫秒实际上也是达不到的。根据HTML的标准,最低是4毫秒。
需要注意的是,此函数是每隔一段时间将回调函数放入Event Loop线程。
一旦setInterval的回调函数fn执行时间超过了延迟时间ms,那么就完全看不出来有时间间隔了
<span style="font-size: 14px;">micro-task(微任务)</span>
与 <span style="font-size: 14px;">macro-task(宏任务)</span>
<span style="font-size: 14px;">Event Loop线程</span>
中包含任务队列(用来对不同优先级的异步事件进行排序),而任务队列又分为<span style="font-size: 14px;">macro-task(宏任务)</span>
与<span style="font-size: 14px;">micro-task(微任务)</span>
,在最新标准中,它们被分别称为<span style="font-size: 14px;">task</span>
与<span style="font-size: 14px;">jobs</span>
。
macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)
setTimeout/Promise等我们称之为<span style="font-size: 14px;">任务源</span>
。而进入任务队列的是他们指定的具体执行任务(回调函数)。
来自不同的任务源的任务会进入到不同的任务队列中,而不同的任务队列执行过程如下:
执行过程如下:
JavaScript引擎首先从<span style="font-size: 14px;">macro-task</span>
中取出第一个任务,
执行完毕后,将<span style="font-size: 14px;">micro-task</span>
中的所有任务取出,按顺序全部执行;
然后再从<span style="font-size: 14px;">macro-task</span>
中取下一个,
执行完毕后,再次将<span style="font-size: 14px;">micro-task</span>
中的全部取出;
循环往复,直到两个队列中的任务都取完。
<span style="font-size: 14px;">console.log("start");<br>var promise = new Promise((resolve) => {<br> console.log("promise start..");<br> resolve("promise");<br>}); //3<br>promise.then((val) => console.log(val));<br>setTimeout(()=>{console.log("setTime1")},0);<br>console.log("test end...")<br></span>
这里我们按顺序来分析。
整体script代码作为一个宏任务进入主线程,运行<span style="font-size: 14px;">console.log("start");</span>
。
然后遇到<span style="font-size: 14px;">Promises</span>
直接运行<span style="font-size: 14px;">console.log("promise start..")</span>
。
然后遇到<span style="font-size: 14px;">promise.then</span>
,存入到<span style="font-size: 14px;">micro-task队列</span>
中。
然后遇到<span style="font-size: 14px;">setTimeout</span>
,存入到<span style="font-size: 14px;">macro-task队列</span>
中。
于然后运行<span style="font-size: 14px;">console.log("test end...")</span>
;
在这一轮中,宏任务运行结束,运行micro-task队列中的 <span style="font-size: 14px;">promise.then</span>
,输出<span style="font-size: 14px;">promise</span>
取出<span style="font-size: 14px;">macro-task队列</span>
中的<span style="font-size: 14px;">setTimeout</span>
,运行<span style="font-size: 14px;">console.log("setTime1");</span>
输出的顺序就是
<span style="font-size: 14px;">// start<br>// promise start<br>// test end...<br>// promise<br>//setTime1<br></span>
async function testSometing() { console.log("执行testSometing"); return "testSometing"; } async function testAsync() { console.log("执行testAsync"); return Promise.resolve("hello async"); } async function test() { console.log("test start..."); const v1 = await testSometing(); console.log(v1); const v2 = await testAsync(); console.log(v2); console.log(v1, v2); } test(); var promise = new Promise((resolve) => { console.log("promise start.."); resolve("promise"); }); //3 promise.then((val) => console.log(val)); setTimeout(()=>{console.log("setTime1")},3000); console.log("test end...")
相关推荐:
Explication détaillée de l'ordre d'exécution du code en JavaScript
Explication détaillée des résultats d'exécution de l'ordre de chargement des classes en Java
Explication détaillée du code d'implémentation de PHP exécutant des programmes externes
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!