Dieser Artikel teilt Ihnen relevante Wissenspunkte über den JavaScript-Ereignisschleifenmechanismus mit. Freunde, die interessiert sind, können ihn lernen und darauf verweisen.
Wie wir alle wissen, ist JavaScript eine Single-Threaded-Sprache. Obwohl Web-Worker in HTML5 vorgeschlagen wurde, ändert dies nichts am Kern von JavaScript, das Single-Threaded ist. Siehe diese Passage in der HTML-Spezifikation:
Um Ereignisse, Benutzerinteraktion, Skripte, Rendering, Netzwerk usw. zu koordinieren, müssen Benutzeragenten Ereignisschleifen verwenden, wie in diesem Abschnitt beschrieben. Es gibt zwei Arten von Ereignisschleifen: solche zum Durchsuchen von Kontexten und solche für Arbeiter.
Um Ereignisse, Benutzerinteraktionen, Skripte, UI-Rendering und Netzwerkverarbeitung zu koordinieren, müssen Benutzer-Engines Ereignisschleifen verwenden. Die Ereignisschleife besteht aus zwei Typen: einer basierend auf dem Browsing-Kontext und einer basierend auf dem Worker, die beide unabhängig voneinander ausgeführt werden. Der folgende Artikel konzentriert sich anhand eines Beispiels auf den Ereignisschleifenmechanismus basierend auf dem Browserkontext.
Sehen Sie sich den folgenden JavaScript-Code an:
console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); Promise.resolve().then(function() { console.log('promise1'); }).then(function() { console.log('promise2'); }); console.log('script end');
Erraten Sie zunächst die Ausgabesequenz dieses Codes und geben Sie sie dann in die Browserkonsole ein, um zu sehen, ob die tatsächliche Ausgabesequenz mit Ihrer übereinstimmt. Ist die erratene Reihenfolge konsistent? Wenn ja, bedeutet dies, dass Sie noch ein gewisses Verständnis für den JavaScript-Ereignisschleifenmechanismus haben. Lesen Sie weiter, um Ihr Wissen zu festigen. Wenn die tatsächliche Ausgabereihenfolge nicht mit Ihrer Vermutung übereinstimmt, werden Ihre Fragen im folgenden Teil dieses Artikels beantwortet .
Aufgabenwarteschlange
Alle Aufgaben können in synchrone Aufgaben und asynchrone Aufgaben unterteilt werden, wie der Name schon sagt. Synchrone Aufgaben werden im Allgemeinen direkt in den Hauptthread eingegeben Ausführung; asynchrone Aufgaben sind Aufgaben, die asynchron ausgeführt werden, wie z. B. Ajax-Netzwerkanforderungen, SetTimeout-Timing-Funktionen usw., bei denen es sich um asynchrone Aufgaben handelt, die über den Mechanismus der Aufgabenwarteschlange (Ereigniswarteschlange) koordiniert werden. Konkret können Sie die folgende Abbildung zur groben Erläuterung verwenden:
Synchrone und asynchrone Aufgaben betreten jeweils unterschiedliche Ausführungsumgebungen und betreten synchron den Hauptthread, dh die Hauptausführung Stapeln und asynchron in die Ereigniswarteschlange eingeben. Wenn die Aufgaben im Hauptthread nach der Ausführung leer sind, werden die entsprechenden Aufgaben aus der Ereigniswarteschlange gelesen und zur Ausführung an den Hauptthread weitergeleitet. Die kontinuierliche Wiederholung des oben genannten Prozesses nennen wir Ereignisschleife.
In der Ereignisschleife wird jede Schleifenoperation als Tick bezeichnet. Durch Lesen der Spezifikation können wir erkennen, dass das Aufgabenverarbeitungsmodell jedes Ticks relativ komplex ist:
Wählen Sie in diesem Häkchen die älteste Aufgabe aus, die zuerst in die Warteschlange kommt, und führen Sie sie (einmal) aus, wenn es eine gibt
Überprüfen Sie, ob Mikrotasks vorhanden sind. falls vorhanden, dann mit der Ausführung fortfahren, bis die Microtask-Warteschlange gelöscht ist
Rendering aktualisieren
Der Hauptthread wiederholt die obigen Schritte
Sie können den Prozess mit einem Bild veranschaulichen:
Ich glaube, hier wird jemand fragen wollen, was Mikrotasks sind. Die Spezifikation schreibt vor, dass Aufgaben aufgeteilt werden In zwei Kategorien unterteilt, nämlich Makroaufgabe (Makroaufgabe) und Mikroaufgabe (Mikroaufgabe). Nach Abschluss jeder Makroaufgabe müssen alle Mikroaufgaben gelöscht werden. In einigen Artikeln wird dies auch nicht als Aufgabe bezeichnet Unterscheiden Sie zwischen ihnen. Die in den folgenden Artikeln genannten Aufgaben werden alle als Makroaufgaben betrachtet.
(Makro-)Aufgabe umfasst hauptsächlich: Skript (Gesamtcode), setTimeout, setInterval, E/A, interaktive UI-Ereignisse, setImmediate (Node.js-Umgebung)
Mikroaufgabe umfasst hauptsächlich: Versprechen, MutaionObserver, process.nextTick (Node.js-Umgebung)
setTimeout/Promise und andere APIs sind Aufgabenquellen, und was in die Aufgabenwarteschlange gelangt, ist die von ihnen angegebene spezifische Ausführungsaufgabe. Aufgaben aus verschiedenen Aufgabenquellen werden in unterschiedliche Aufgabenwarteschlangen eingegeben. Unter diesen haben setTimeout und setInterval denselben Ursprung.
Analyse des Beispielcodes
Es gibt Tausende von Wörtern, daher ist es besser, es anhand von Beispielen klar zu erklären. Nachfolgend können wir den Spezifikationen folgen und das obige Beispiel Schritt für Schritt analysieren. Posten Sie zunächst den Beispielcode (um Ihnen das Scrollen nach oben zu ersparen).
console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0); Promise.resolve().then(function() { console.log('promise1'); }).then(function() { console.log('promise2'); }); console.log('script end');
Das Gesamtskript tritt als erste Makroaufgabe in den Hauptthread ein, trifft auf console.log und gibt den Skriptstart aus.
Trifft auf setTimeout und seine Rückruffunktion wird verteilt to In der Makro-Task-Ereigniswarteschlange trifft
auf ein Versprechen, und seine Then-Funktion wird der Mikrotask-Ereigniswarteschlange zugewiesen, die als then1 aufgezeichnet wird. Dann trifft es erneut auf die Then-Funktion und weist sie zu Es wird in die Ereigniswarteschlange der Mikroaufgabe geschrieben, wenn es auf console.log stößt und das Skriptende ausgibt
Zu diesem Zeitpunkt gibt es drei Aufgaben, wie in der folgenden Tabelle dargestellt:
执行微任务,首先执行then1,输出 promise1, 然后执行 then2,输出 promise2,这样就清空了所有微任务
执行 setTimeout 任务,输出 setTimeout 至此,输出的顺序是:script start, script end, promise1, promise2, setTimeout
so,你猜对了吗?
看看你掌握了没
再来一个题目,来做个练习:
console.log('script start'); setTimeout(function() { console.log('timeout1'); }, 10); new Promise(resolve => { console.log('promise1'); resolve(); setTimeout(() => console.log('timeout2'), 10); }).then(function() { console.log('then1') }) console.log('script end');
这个题目就稍微有点复杂了,我们再分析下:
首先,事件循环从宏任务 (macrotask) 队列开始,最初始,宏任务队列中,只有一个 scrip t(整体代码)任务;当遇到任务源 (task source) 时,则会先分发任务到对应的任务队列中去。所以,就和上面例子类似,首先遇到了console.log,输出 script start; 接着往下走,遇到 setTimeout 任务源,将其分发到任务队列中去,记为 timeout1; 接着遇到 promise,new promise 中的代码立即执行,输出 promise1, 然后执行 resolve ,遇到 setTimeout ,将其分发到任务队列中去,记为 timemout2, 将其 then 分发到微任务队列中去,记为 then1; 接着遇到 console.log 代码,直接输出 script end 接着检查微任务队列,发现有个 then1 微任务,执行,输出then1 再检查微任务队列,发现已经清空,则开始检查宏任务队列,执行 timeout1,输出 timeout1; 接着执行 timeout2,输出 timeout2 至此,所有的都队列都已清空,执行完毕。其输出的顺序依次是:script start, promise1, script end, then1, timeout1, timeout2
用流程图看更清晰:
总结
有个小 tip:从规范来看,microtask 优先于 task 执行,所以如果有需要优先执行的逻辑,放入microtask 队列会比 task 更早的被执行。
最后的最后,记住,JavaScript 是一门单线程语言,异步操作都是放到事件循环队列里面,等待主执行栈来执行的,并没有专门的异步执行线程。
以上就是本章的全部内容,更多相关教程请访问JavaScript视频教程!
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung des JavaScript-Ereignisschleifenmechanismus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!