Heim  >  Artikel  >  Web-Frontend  >  Warum gibt es Asynchronität? Was ist eine Ereigniswarteschlange?

Warum gibt es Asynchronität? Was ist eine Ereigniswarteschlange?

PHP中文网
PHP中文网Original
2017-06-21 13:26:273216Durchsuche

Einführung

Ich glaube, jeder, der JavaScript studiert hat, weiß, dass es sich um eine Single-Threaded-Sprache handelt, was bedeutet, dass JS keine Multithread-Programmierung durchführen kann, aber JS über ein allgegenwärtiges Asynchronous-Konzept verfügt . In den frühen Tagen verstanden viele Menschen Asynchronität als ein Programmiermodell, das Multithreading ähnelt. Tatsächlich gibt es große Unterschiede zwischen ihnen. Um Asynchronität vollständig zu verstehen, muss man den laufenden Kern von JS verstehen – Ereignisschleife . Ich hatte vorher nur ein begrenztes Verständnis der Ereignisschleife. Erst als ich Philip Roberts' Rede „Was zum Teufel ist die Ereignisschleife“ sah, hatte ich ein umfassendes Verständnis der Ereignisschleife, also wollte ich eine Einführung dazu schreiben JS Der Artikel zur Ereignisschleife dient zum Lernen und Nachschlagen für jedermann.

1. Warum gibt es Asynchronität?

Warum gibt es Asynchronität in JS? Stellen wir uns vor, was passiert, wenn wir den Code synchron ausführen:

1 $.get(url, function(data) {2     //do something3 });

Wenn wir Ajax zur Kommunikation verwenden, verwenden wir alle standardmäßig die Asynchronität, aber wenn Was passiert, wenn wir es festlegen synchron ausführen? Wenn Sie selbst ein kleines Testprogramm schreiben und den Hintergrundcode um 5 Sekunden verzögern, werden Sie feststellen, dass der Browser blockiert, bis Ajax antwortet, und dann normal ausgeführt wird. Dies ist das Hauptproblem, das der asynchrone Modus lösen muss: Wie kann der Browser dazu gebracht werden, Aufgaben nicht blockierend auszuführen? Stellen Sie sich vor, dass unsere Wartezeit bei der synchronen Ausführung der Ajax-Anforderung möglicherweise sehr schnell oder sehr langsam ist oder dass der Browser bei einer unbekannten Mission blockiert , das ist es, was wir nicht sehen wollen. Wir hoffen also, dass es eine Möglichkeit gibt, das Programm asynchron zu verarbeiten. Wir müssen uns nicht darum kümmern, wann eine Ajax-Anfrage abgeschlossen wird, oder es kann sein, dass sie nie antwortet. Wir müssen nur wissen, wie wir mit der Anfrage umgehen sollen. und warten Sie auf die Antwort. In dieser Zeit können wir auch andere Arbeiten erledigen. Daher JavaScript-Ereignisschleife.

2. Was ist eine Ereigniswarteschlange?

Schauen wir uns zunächst einen einfachen Codeabschnitt an:

1 console.log("script start");2 3 setTimeout(function () {4     console.log("setTimeout");5 }, 1000);6 7 console.log("script end");

Sie können die Ergebnisse hier ansehen:

Wir können sehen: Erstens , gibt das Programm 'script start' und 'script end' aus und etwa 1 Sekunde später gibt es 'setTimeout' aus. Das 'Skriptende' des Programms wartet nicht auf die Ausgabe von 1 Sekunde, sondern gibt sofort aus. Dies liegt daran, dass setTimeout eine asynchrone Funktion ist. Das heißt, wenn wir eine Verzögerungsfunktion festlegen, wird das aktuelle Skript nicht blockiert, sondern nur in der Ereignistabelle des Browsers aufgezeichnet und das Programm wird weiterhin ausgeführt. Wenn die Verzögerungszeit abgelaufen ist, fügt die Ereignistabelle die Rückruffunktion zur -Ereigniswarteschlange (Aufgabenwarteschlange) hinzu. Nachdem die Ereigniswarteschlange die Aufgabe erhalten hat, wird sie in den -Ausführungsstapel verschoben (Stapel) In der Mitte führt der Ausführungsstapel die Aufgabe aus und gibt 'setTimeout' aus.

Die Ereigniswarteschlange ist eine Warteschlange, in der die auszuführenden Aufgaben streng in chronologischer Reihenfolge gespeichert werden. Die Aufgaben an der Spitze der Warteschlange werden zuerst ausgeführt und die Aufgaben am Ende der Warteschlange wird zuletzt ausgeführt. Die Ereigniswarteschlange führt jeweils nur eine Aufgabe aus. Nachdem die Aufgabe abgeschlossen ist, wird die nächste Aufgabe ausgeführt. Der Ausführungsstapel ist ein laufender Container, ähnlich einem Funktionsaufrufstapel. Wenn der Ausführungsstapel leer ist, überprüft die JS-Engine die Ereigniswarteschlange. Wenn sie nicht leer ist, schiebt die Ereigniswarteschlange die erste Aufgabe zur Ausführung in den Ausführungsstapel.

Nehmen wir nun eine kleine Änderung am obigen Code vor:

1 console.log("script start");2 3 setTimeout(function () {4     console.log("setTimeout");5 }, 0);6 7 console.log("script end");

Setzen Sie die Verzögerungszeit auf 0 und sehen Sie, in welcher Reihenfolge das Programm ausgibt? Egal wie viel Verzögerungszeit wir einstellen, 'setTimeout' wird immer nach 'script end' ausgegeben. Einige Browser haben möglicherweise eine minimale Verzögerungszeit, einige sind 15 ms, andere 10 ms. Dies wird in vielen Büchern erwähnt. Dies kann den Schülern eine Illusion vermitteln: Da das Programm sehr schnell läuft und eine minimale Verzögerungszeit hat, ist 'setTimeout ' wird nach 'script end' ausgegeben. Jetzt ändern wir es ein wenig, um Ihre Illusion zu zerstreuen:

 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");

  你可以在这里查看结果:


   可以看出,无论后面我们做了多少延迟性的工作,'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 });

Nach dem Testen können wir sehen, dass bei Verwendung der synchronen Durchquerungsmethode eine Blockierung auftritt, wenn die Array-Länge auf 3 Stellen ansteigt, die asynchrone Durchquerung jedoch nicht blockiert (es sei denn, die Array-Länge ist sehr groß). Dies liegt daran, dass der Computer nicht genügend Speicherplatz vorhanden). Dies liegt daran, dass die synchrone Durchlaufmethode eine separate Aufgabe ist. Diese Aufgabe durchläuft alle Array-Elemente, bevor die nächste Aufgabe gestartet wird. Die asynchrone Durchlaufmethode teilt jeden Durchlauf in eine separate Aufgabe auf. Jede Aufgabe durchläuft nur ein Array-Element, sodass unser Browser zwischen den einzelnen Aufgaben rendern kann, sodass keine Blockierung auftritt. Die folgende Demo zeigt, was vor und nach der asynchronen Durchquerung passiert:


Zusammenfassung

Nun glaube ich, dass Sie das wahre Gesicht von JavaScript verstanden haben. JavaScript ist eine Single-Thread-Sprache, aber ihre Ereignisschleifenfunktion ermöglicht es uns, Programme asynchron auszuführen. Diese asynchronen Programme sind nacheinander unabhängige Aufgaben. Zu diesen Aufgaben gehören setTimeout, setInterval, Ajax, EventListener usw. In Bezug auf die Ereignisschleife müssen wir die folgenden Punkte beachten:

  • Die Ereigniswarteschlange schiebt Aufgaben streng in chronologischer Reihenfolge in den Ausführungsstapel; > Wenn der Ausführungsstapel leer ist, überprüft der Browser weiterhin die Ereigniswarteschlange. Wenn sie nicht leer ist, wird die erste Aufgabe herausgenommen.

  • Nach dem Ende jeder Aufgabe Der Browser rendert die Seite.

  • Die Demo dieses Artikels wird auf jsfiddle platziert. Wenn Sie ihn erneut drucken müssen, geben Sie einfach die Quelle an. Sollten Ihnen in diesem Artikel Fehler auffallen, weisen Sie diese bitte im Kommentarbereich darauf hin.

Das obige ist der detaillierte Inhalt vonWarum gibt es Asynchronität? Was ist eine Ereigniswarteschlange?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn