Maison > interface Web > js tutoriel > Pourquoi y a-t-il une asynchronie ? Qu'est-ce qu'une file d'attente d'événements ?

Pourquoi y a-t-il une asynchronie ? Qu'est-ce qu'une file d'attente d'événements ?

PHP中文网
Libérer: 2017-06-21 13:26:27
original
3281 Les gens l'ont consulté

Introduction

Je crois que tous ceux qui ont étudié JavaScript savent qu'il s'agit d'un langage monothread, ce qui signifie que JS ne peut pas effectuer de programmation multithread, mais JS a un concept asynchrone omniprésent . Au début, beaucoup de gens comprenaient l'asynchrone comme un modèle de programmation similaire au multi-threading. En fait, il existe de grandes différences entre eux. Pour bien comprendre l'asynchrone, vous devez comprendre le cœur opérationnel de JS - Boucle d'événements. . Avant, je n'avais qu'une compréhension limitée de la boucle d'événements. Ce n'est que lorsque j'ai regardé le discours de Philip Roberts, "Qu'est-ce que c'est que la boucle d'événements, de toute façon ?" JS L'article sur la boucle d'événements est destiné à l'apprentissage et à la référence de chacun.

1. Pourquoi y a-t-il une asynchronie ?

Pourquoi y a-t-il de l'asynchronie en JS ? Imaginons ce qui se passera si nous exécutons le code de manière synchrone :

1 $.get(url, function(data) {2     //do something3 });
Copier après la connexion

Lorsque nous utilisons ajax pour communiquer, nous le définissons tous par défaut comme asynchrone, mais si que se passe-t-il si nous le définissons exécuter de manière synchrone? Si vous écrivez vous-même un petit programme de test et retardez le code d'arrière-plan de 5 secondes, vous constaterez que le navigateur se bloquera jusqu'à ce qu'Ajax réponde, puis fonctionnera normalement. C'est le principal problème que le mode asynchrone doit résoudre : comment faire en sorte que le navigateur exécute des tâches de manière non bloquante. Imaginez que si nous exécutons la requête ajax de manière synchrone, notre temps d'attente est une quantité inconnue. Il peut être très rapide ou très lent dans la communication réseau, ou il peut ne jamais répondre. Cela entraînera également le blocage du navigateur lors d'une mission inconnue. c'est ce que nous ne voulons pas voir. Nous espérons donc qu'il existe un moyen de gérer le programme de manière asynchrone. Nous n'avons pas besoin de nous soucier du moment où une requête ajax sera terminée, ou même elle risque de ne jamais répondre. Nous avons seulement besoin de savoir comment gérer la requête une fois qu'elle a répondu. et attendre la réponse. Nous pouvons également effectuer d'autres travaux pendant cette période. Par conséquent, la boucle d’événements JavaScript.

2. Qu'est-ce qu'une file d'attente d'événements ?

Tout d'abord, regardons un simple morceau de code :

1 console.log("script start");2 3 setTimeout(function () {4     console.log("setTimeout");5 }, 1000);6 7 console.log("script end");
Copier après la connexion

Vous pouvez voir les résultats ici :

Nous pouvons voir, d'abord , le programme affiche 'script start' et 'script end', et environ 1 seconde plus tard, il affiche 'setTimeout'. La 'fin du script' du programme n'attend pas les 1 pour sortir, mais sort immédiatement. En effet, setTimeout est une fonction asynchrone. Cela signifie que lorsque nous définissons une fonction de retard, le script actuel ne bloquera pas, il sera uniquement enregistré dans la table des événements du navigateur et le programme continuera à s'exécuter. Une fois le délai écoulé, la table des événements ajoutera la fonction de rappel à la file d'attente des événements (file d'attente des tâches) Une fois que la file d'attente des événements aura obtenu la tâche, elle la poussera dans la pile d'exécution. (pile) Au milieu, la pile d'exécution exécute la tâche et génère 'setTimeout'.

La file d'attente des événements est une file d'attente qui stocke les tâches à exécuter. Les tâches sont exécutées strictement dans l'ordre chronologique. Les tâches en tête de file d'attente seront exécutées en premier, et les tâches en fin de file d'attente. sera exécuté en dernier. La file d'attente d'événements n'exécute qu'une tâche à la fois. Une fois la tâche terminée, la tâche suivante est exécutée. La pile d'exécution est un conteneur en cours d'exécution similaire à une pile d'appels de fonction. Lorsque la pile d'exécution est vide, le moteur JS vérifie la file d'attente d'événements. Si elle n'est pas vide, la file d'attente d'événements pousse la première tâche dans la pile d'exécution pour s'exécuter.

Maintenant, apportons une petite modification au code ci-dessus :

1 console.log("script start");2 3 setTimeout(function () {4     console.log("setTimeout");5 }, 0);6 7 console.log("script end");
Copier après la connexion

Réglez le temps de retard sur 0 et voyez dans quel ordre le programme sortira ? Quel que soit le délai que nous définissons, 'setTimeout' sera toujours affiché après 'fin du script'. Certains navigateurs peuvent avoir un temps de retard minimum, certains sont de 15 ms, d'autres de 10 ms. Ceci est mentionné dans de nombreux livres. Cela peut donner une illusion aux étudiants : parce que le programme s'exécute très vite et a un temps de retard minimum, donc 'setTimeout. ' sera affiché après 'fin du script'. Maintenant, modifions cela un peu pour dissiper votre illusion :

 1 console.log("script start"); 2  3 setTimeout(function () { 4     console.log("setTimeout"); 5 }, 0); 6  7 //具体数字不定,这取决于你的硬件配置和浏览器 8 for(var i = 0; i < 999999999; i ++){ 9     //do something10 }11 12 console.log("script end");
Copier après la connexion

  你可以在这里查看结果:


   可以看出,无论后面我们做了多少延迟性的工作,'setTimeout' 总是会在 'script end' 之后输出。所以究竟发生了什么?这是因为 setTimeout 的回调函数只是会被添加至事件队列,而不是立即执行。由于当前的任务没有执行结束,所以 setTimeout 任务不会执行,直到输出了 'script end' 之后,当前任务执行完毕,执行栈为空,这时事件队列才会把 setTimeout 回调函数压入执行栈执行。


  执行栈则像是函数的调用栈,是一个树状的栈:


 三、事件队列有何作用?

  通过以上的 demo 相信同学们都会对事件队列和执行栈有了一个基本的认识,那么事件队列有何作用?最简单易懂的一点就是之前我们所提到的异步问题。由于 JS 是单线程的,同步执行任务会造成浏览器的阻塞,所以我们将 JS 分成一个又一个的任务,通过不停的循环来执行事件队列中的任务。这就使得当我们挂起某一个任务的时候可以去做一些其他的事情,而不需要等待这个任务执行完毕。所以事件循环的运行机制大致分为以下步骤:

  1.   检查事件队列是否为空,如果为空,则继续检查;如不为空,则执行 2;

  2.   取出事件队列的首部,压入执行栈;

  3.        执行任务;

  4.        检查执行栈,如果执行栈为空,则跳回第 1 步;如不为空,则继续检查;

  然而目前为止我们讨论的仅仅是 JS 引擎如何执行 JS 代码,现在我们结合 Web APIs 来讨论事件循环在当中扮演的角色。

  在开始我们讨论过 ajax 技术的异步性和同步性,通过事件循环机制,我们则不需要等待 ajax 响应之后再进行工作。我们则是设置一个回调函数,将 ajax 请求挂起,然后继续执行后面的代码,至于请求何时响应,对我们的程序不会有影响,甚至它可能永远也不响应,也不会使浏览器阻塞。而当响应成功了以后,浏览器的事件表则会将回调函数添加至事件队列中等待执行。事件监听器的回调函数也是一个任务,当我们注册了一个事件监听器时,浏览器事件表会进行登记,当我们触发事件时,事件表便将回调函数添加至事件队列当中。


  我们知道 DOM 操作会触发浏览器对文档进行渲染,如修改排版规则,修改背景颜色等等,那么这类操作是如何在浏览器当中奏效的?至此我们已经知道了事件循环是如何执行的,事件循环器会不停的检查事件队列,如果不为空,则取出队首压入执行栈执行。当一个任务执行完毕之后,事件循环器又会继续不停的检查事件队列,不过在这间,浏览器会对页面进行渲染。这就保证了用户在浏览页面的时候不会出现页面阻塞的情况,这也使 JS 动画成为可能, jQuery 动画在底层均是使用 setTimeout 和 setInterval 来进行实现。想象一下如果我们同步的执行动画,那么我们不会看见任何渐变的效果,浏览器会在任务执行结束之后渲染窗口。反之我们使用异步的方法,浏览器会在每一个任务执行结束之后渲染窗口,这样我们就能看见动画的渐变效果了。

  考虑如下两种遍历方式:

 1 var arr = new Array(999); 2 arr.fill(1); 3 function asyncForEach(array, handler){ 4     var t = setInterval(function () { 5         if(array.length === 0){ 6             clearInterval(t); 7         }else { 8             handler(arr.shift()); 9         }10     }, 0);11 }12 13 //异步遍历14 asyncForEach(arr, function (value) {15     console.log(value);16 });17 18 //同步遍历19 arr.forEach(function (value, index, arr) {20     console.log(value);21 });
Copier après la connexion

Après les tests, nous pouvons voir qu'en utilisant la méthode de parcours synchrone, lorsque la longueur du tableau atteint 3 chiffres, un blocage se produira, mais le parcours asynchrone ne bloquera pas (sauf si la longueur du tableau est très grande, alors c'est parce que l'ordinateur ne dispose pas de suffisamment d'espace mémoire). En effet, la méthode de parcours synchrone est une tâche distincte. Cette tâche parcourra tous les éléments du tableau avant de démarrer la tâche suivante. La méthode de parcours asynchrone divise chaque parcours en une tâche distincte. Chaque tâche ne traverse qu'un seul élément du tableau, donc entre chaque tâche, notre navigateur peut effectuer le rendu, nous ne verrons donc pas de blocage. La démo suivante montre ce qui se passe avant et après le parcours asynchrone :


Résumé

Maintenant, je pense que vous avez compris le vrai visage de JavaScript. JavaScript est un langage monothread, mais sa fonctionnalité de boucle d'événements nous permet d'exécuter des programmes de manière asynchrone. Ces programmes asynchrones sont des tâches indépendantes les unes après les autres. Ces tâches incluent setTimeout, setInterval, ajax, eventListener, etc. Concernant la boucle d'événements, nous devons nous rappeler les points suivants :

  • La file d'attente d'événements pousse les tâches dans la pile d'exécution strictement dans l'ordre chronologique ; > Lorsque la pile d'exécution est vide, le navigateur continuera à vérifier la file d'attente des événements. Si elle n'est pas vide, la première tâche sera supprimée

  • Après la fin de chaque tâche, La le navigateur affichera la page ;

  • La démo de cet article est placée sur jsfiddle. Si vous avez besoin de le réimprimer, indiquez simplement la source. Si vous constatez des erreurs dans cet article, merci de les signaler dans la zone commentaires.

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:php.cn
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