Maison  >  Article  >  interface Web  >  À propos de l'utilisation des exemples de programmation synchrone et asynchrone JavaScript

À propos de l'utilisation des exemples de programmation synchrone et asynchrone JavaScript

伊谢尔伦
伊谢尔伦original
2017-06-16 10:20:012184parcourir

Si vous souhaitez apprendre le javascript en profondeur, jetez un œil à l'article suivant, il peut vous être utile.

Avant-propos

Si vous êtes déterminé à devenir un excellent ingénieur front-end, ou si vous souhaitez apprendre JavaScript en profondeur, la programmation asynchrone est un point de connaissance essentiel, et c'est aussi ce que distingue les débutants, une des bases du front-end intermédiaire ou avancé. Si vous n'avez pas une idée claire de la programmation asynchrone, je vous suggère de passer du temps à apprendre la programmation asynchrone JavaScript. Si vous avez votre propre compréhension de la programmation asynchrone, vous êtes invités à lire cet article et à communiquer ensemble.

Synchronisation et asynchrone

Avant d'introduire l'asynchrone, rappelons que la programmation dite synchrone signifie que l'ordinateur exécute le code ligne par ligne dans l'ordre. bloquera l’exécution du code suivant.

La programmation synchrone est un modèle de requête-réponse typique. Lorsqu'une requête appelle une fonction ou une méthode, elle doit attendre le retour de sa réponse, puis exécuter le code suivant.

Dans des circonstances normales, la programmation synchrone, où le code est exécuté dans l'ordre, peut bien assurer l'exécution du programme. Cependant, dans certains scénarios, comme la lecture du contenu d'un fichier ou la demande de données d'interface serveur, Les opérations ultérieures doivent être effectuées en fonction du contenu des données renvoyées. Le processus de lecture du fichier et de demande d'interface jusqu'à ce que les données soient renvoyées prend du temps. Plus le réseau est mauvais, plus il prend de temps s'il est mis en œuvre de manière synchrone. En programmation, le temps passé à attendre que les données soient renvoyées, JavaScript ne peut pas gérer d'autres tâches. À ce stade, toutes les opérations telles que l'interaction et le défilement des pages seront également bloquées. C'est évidemment extrêmement hostile et inacceptable. où la programmation asynchrone doit montrer ses talents, comme indiqué ci-dessous, la tâche chronophage A bloquera l'exécution de la tâche B et ne pourra pas continuer à exécuter B tant que la tâche A n'est pas terminée :

À propos de lutilisation des exemples de programmation synchrone et asynchrone JavaScript

Lors de l'utilisation de la programmation asynchrone, attendez le retour de la réponse de la tâche en cours avant de revenir, vous pouvez continuer à exécuter le code suivant, c'est-à-dire que la tâche d'exécution en cours ne bloquera pas l'exécution ultérieure.

La programmation asynchrone est différente du mode requête-réponse de la programmation synchrone. Il s'agit d'une sorte de programmation basée sur les événements. Après avoir demandé l'appel d'une fonction ou d'une méthode, il n'est pas nécessaire d'attendre. réponse immédiatement et vous pouvez continuer à effectuer d'autres tâches avant le retour de la réponse à la tâche, l'appelant peut être averti via l'état, les notifications et les rappels.

Multi-threading

Comme expliqué précédemment, la programmation asynchrone peut résoudre efficacement le problème du blocage de la programmation synchrone. Alors, quelles sont les façons de mettre en œuvre la programmation asynchrone ? La méthode habituelle pour réaliser une implémentation asynchrone est le multithreading, tel que C#, ce qui signifie que plusieurs threads sont démarrés en même temps et que différentes opérations peuvent être exécutées en parallèle, comme le montre la figure ci-dessous, alors que la tâche A prend beaucoup de temps. exécutée, la tâche B peut également être exécutée dans le thread deux :

À propos de lutilisation des exemples de programmation synchrone et asynchrone JavaScript

JavaScript monothread

L'environnement d'exécution du langage JavaScript est monothread. -thread exécute un programme, les chemins du programme qu'il emprunte sont organisés dans un ordre consécutif et les précédents doivent être traités. Eh bien, les derniers seront exécutés, et lors de l'utilisation d'une implémentation asynchrone, plusieurs tâches peuvent être exécutées simultanément. Alors, comment implémenter la programmation asynchrone en JavaScript ? La section suivante détaillera son mécanisme asynchrone.

Parallèle et concurrence

Comme mentionné précédemment, les tâches multithread peuvent être exécutées en parallèle, tandis que la programmation asynchrone JavaScript monothread peut réaliser l'exécution simultanée de plusieurs tâches. Il est nécessaire d'expliquer la différence entre le parallélisme. et la concurrence.

  • Parallèle fait référence à plusieurs tâches exécutées en même temps ;

  • La concurrence fait référence à plusieurs tâches exécutées en même temps au sein du même période de temps. Mais à un certain moment, seule une certaine tâche est exécutée

Le nombre de connexions simultanées fait généralement référence au nombre de fois que le navigateur initie une requête au serveur et l'établit ; une connexion TCP, et le serveur l'établit par seconde. Le nombre total de connexions, et si le serveur peut gérer une connexion en 10 ms, alors le nombre de connexions simultanées est de 100.

Mécanisme asynchrone JavaScript

Cette section présente le mécanisme asynchrone JavaScript. Examinons d'abord un exemple :

    for (var i = 0; i < 5; i ++) {
        setTimeout(function(){
            console.log(i);
        }, 0);
    }
    console.log(i);
    //5 ; 5 ; 5 ; 5; 5

Vous devez comprendre que le résultat final est constitué des 5 :

  1. i voici la variable du contexte où se situe la boucle for, et il n'y a qu'un seul i;

  2. i==5 à la fin de la boucle ;

  3. Le gestionnaire d'événements JavaScript monothread n'exécutera pas l'événement suivant tant que le thread n'est pas inactif.

Comme mentionné dans le troisième point ci-dessus, si vous voulez vraiment comprendre setTimeout() dans l'exemple ci-dessus et le mécanisme asynchrone JavaScript, vous devez comprendre la boucle d'événements JavaScript et le modèle de concurrence. .

Modèle de concurrence

Actuellement, nous savons déjà que lorsque JavaScript effectue une tâche asynchrone, il n'a pas besoin d'attendre le retour de la réponse et peut continuer à effectuer d'autres tâches, et lorsque le La réponse revient, vous obtiendrez une notification, un rappel d'exécution ou un gestionnaire d'événements. Alors, comment tout cela se fait-il exactement, et selon quelles règles et dans quel ordre cela fonctionne-t-il ? Ensuite, nous devons répondre à cette question.

Remarque : Il n'y a essentiellement aucune différence entre les rappels et les gestionnaires d'événements, ils sont simplement appelés différemment selon les situations.

Comme mentionné précédemment, la programmation asynchrone JavaScript permet d'exécuter plusieurs tâches simultanément, et la base pour réaliser cette fonction est que JavaScript dispose d'un modèle de concurrence basé sur la boucle d'événements.

Pile et file d'attente

Avant de présenter le modèle de concurrence JavaScript, présentons brièvement la différence entre pile et file d'attente :

  • Tas : une certaine zone dans mémoire Une zone non bloquée, stockant généralement des objets (types de référence)

  • Pile : une structure de données stockée dans l'ordre dernier entré, premier sorti, stockant généralement les paramètres de fonction et les variables de valeur de type de base ; (Accès par valeur)

  • Queue (file d'attente) : structure de données stockée dans l'ordre premier entré, premier sorti.

Boucle d'événement

Le moteur JavaScript est responsable de l'analyse et de l'exécution du code JavaScript, mais il ne peut pas fonctionner seul. Il nécessite généralement un environnement hôte, généralement tel qu'un navigateur ou. Serveur de nœuds, le thread unique mentionné ci-dessus fait référence à la création d'un thread unique dans ces environnements hôtes, fournissant un mécanisme pour appeler le moteur JavaScript pour terminer la planification et l'exécution de plusieurs blocs de code JavaScript (oui, les codes JavaScript sont tous en blocs ( exécuté), ce mécanisme est appelé boucle d'événement (Event Loop).

注:这里的事件与DOM事件不要混淆,可以说这里的事件包括DOM事件,所有异步操作都是一个事件,诸如ajax请求就可以看作一个request请求事件。

JavaScript执行环境中存在的两个结构需要了解:

  • 消息队列(message queue),也叫任务队列(task queue):存储待处理消息及对应的回调函数或事件处理程序;

  • 执行栈(execution context stack),也可以叫执行上下文栈:JavaScript执行栈,顾名思义,是由执行上下文组成,当函数调用时,创建并插入一个执行上下文,通常称为执行栈帧(frame),存储着函数参数和局部变量,当该函数执行结束时,弹出该执行栈帧;

注:关于全局代码,由于所有的代码都是在全局上下文执行,所以执行栈顶总是全局上下文就很容易理解,直到所有代码执行完毕,全局上下文退出执行栈,栈清空了;也即是全局上下文是第一个入栈,最后一个出栈。

任务

分析事件循环流程前,先阐述两个概念,有助于理解事件循环:同步任务和异步任务。

任务很好理解,JavaScript代码执行就是在完成任务,所谓任务就是一个函数或一个代码块,通常以功能或目的划分,比如完成一次加法计算,完成一次ajax请求;很自然的就分为同步任务和异步任务。同步任务是连续的,阻塞的;而异步任务则是不连续,非阻塞的,包含异步事件及其回调,当我们谈及执行异步任务时,通常指执行其回调函数。

事件循环流程

关于事件循环流程分解如下:

  1. 宿主环境为JavaScript创建线程时,会创建堆(heap)和栈(stack),堆内存储JavaScript对象,栈内存储执行上下文;

  2. 栈内执行上下文的同步任务按序执行,执行完即退栈,而当异步任务执行时,该异步任务进入等待状态(不入栈),同时通知线程:当触发该事件时(或该异步操作响应返回时),需向消息队列插入一个事件消息;

  3. 当事件触发或响应返回时,线程向消息队列插入该事件消息(包含事件及回调);

  4. 当栈内同步任务执行完毕后,线程从消息队列取出一个事件消息,其对应异步任务(函数)入栈,执行回调函数,如果未绑定回调,这个消息会被丢弃,执行完任务后退栈;

  5. 当线程空闲(即执行栈清空)时继续拉取消息队列下一轮消息(next tick,事件循环流转一次称为一次tick)。

使用代码可以描述如下:

    var eventLoop = [];    var event;    var i = eventLoop.length - 1; // 后进先出

    while(eventLoop[i]) {        event = eventLoop[i--]; 
        if (event) { // 事件回调存在
            event();
        }        // 否则事件消息被丢弃
    }

这里注意的一点是等待下一个事件消息的过程是同步的。

并发模型与事件循环
    var ele = document.querySelector(&#39;body&#39;);    function clickCb(event) {        console.log(&#39;clicked&#39;);
    }    function bindEvent(callback) {
        ele.addEventListener(&#39;click&#39;, callback);
    }    

    bindEvent(clickCb);

针对如上代码我们可以构建如下并发模型:

À propos de lutilisation des exemples de programmation synchrone et asynchrone JavaScript

如上图,当执行栈同步代码块依次执行完直到遇见异步任务时,异步任务进入等待状态,通知线程,异步事件触发时,往消息队列插入一条事件消息;而当执行栈后续同步代码执行完后,读取消息队列,得到一条消息,然后将该消息对应的异步任务入栈,执行回调函数;一次事件循环就完成了,也即处理了一个异步任务。

再谈À propos de lutilisation des exemples de programmation synchrone et asynchrone JavaScript

了解了JavaScript事件循环后我们再看前文关于À propos de lutilisation des exemples de programmation synchrone et asynchrone JavaScript的例子就比较清晰了:

À propos de lutilisation des exemples de programmation synchrone et asynchrone JavaScript所表达的意思是:等待0秒后(这个时间由第二个参数值确定),往消息队列插入一条定时器事件消息,并将其第一个参数作为回调函数;而当执行栈内同步任务执行完毕时,线程从消息队列读取消息,将该异步任务入栈,执行;线程空闲时再次从消息队列读取消息。

再看一个实例:

    var start = +new Date();    var arr = [];

    setTimeout(function(){        console.log(&#39;time: &#39; + (new Date().getTime() - start));
    },10);    for(var i=0;i<=1000000;i++){
        arr.push(i);
    }

执行多次输出如下:

À propos de lutilisation des exemples de programmation synchrone et asynchrone JavaScript

setTimeout异步回调函数里我们输出了异步任务注册到执行的时间,发现并不等于我们指定的时间,而且两次时间间隔也都不同,考虑以下两点:

  • 在读取消息队列的消息时,得等同步任务完成,这个是需要耗费时间的;

  • 消息队列先进先出原则,读取此异步事件消息之前,可能还存在其他消息,执行也需要耗时;

所以异步执行时间不精确是必然的,所以我们有必要明白无论是同步任务还是异步任务,都不应该耗时太长,当一个消息耗时太长时,应该尽可能的将其分割成多个消息。

Web Workers

每个Web Worker或一个跨域的iframe都有各自的堆栈和消息队列,这些不同的文档只能通过postMessage方法进行通信,当一方监听了message事件后,另一方才能通过该方法向其发送消息,这个message事件也是异步的,当一方接收到另一方通过postMessage方法发送来的消息后,会向自己的消息队列插入一条消息,而后续的并发流程依然如上文所述。

JavaScript异步实现

关于JavaScript的异步实现,以前有:回调函数,发布订阅模式,Promise三类,而在ES6中提出了生成器(Generator)方式实现,关于回调函数和发布订阅模式实现可参见另一篇文章,后续将推出一篇详细介绍Promise和Generator。

以上就是javascript同步与异步的全部内容了,感谢大家的阅读。À propos de l'utilisation des exemples de programmation synchrone et asynchrone 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!

Déclaration:
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