Sie wissen wahrscheinlich bereits, dass JavaScript eine Single-Threaded-Programmiersprache ist. Dies bedeutet, dass JavaScript in einem einzelnen Hauptthread in einem Webbrowser oder Node.js ausgeführt wird. Die Ausführung in einem einzelnen Hauptthread bedeutet, dass jeweils nur ein Teil des JavaScript-Codes ausgeführt wird.
Die Ereignisschleife in JavaScript spielt eine wichtige Rolle bei der Bestimmung, wie Code im Hauptthread ausgeführt wird. Die Ereignisschleife ist für Dinge wie die Ausführung von Code sowie die Erfassung und Verarbeitung von Ereignissen verantwortlich. Es übernimmt auch die Ausführung aller in der Warteschlange befindlichen Unteraufgaben.
In diesem Tutorial lernen Sie die Grundlagen von Ereignisschleifen in JavaScript.
Um zu verstehen, wie die Ereignisschleife funktioniert, müssen Sie drei wichtige Begriffe verstehen.
Ein Aufrufstapel ist einfach ein Stapel von Funktionsaufrufen, der den Ausführungskontext einer Funktion verfolgt. Der Stack folgt dem Last-In-First-Out-Prinzip (LIFO), was bedeutet, dass die zuletzt aufgerufene Funktion als erste ausgeführt wird.
Eine Warteschlange enthält eine Reihe von Aufgaben, die von JavaScript ausgeführt werden. Aufgaben in dieser Warteschlange können dazu führen, dass Funktionen aufgerufen werden, die dann auf dem Stapel abgelegt werden. Die Verarbeitung der Warteschlange beginnt erst, wenn der Stapel leer ist. Elemente in der Warteschlange folgen dem First-in-First-out-Prinzip (FIFO). Das bedeutet, dass die ältesten Aufgaben zuerst erledigt werden.
Heap ist im Grunde ein großer Speicherbereich, in dem Objekte gespeichert und zugewiesen werden. Sein Hauptzweck besteht darin, Daten zu speichern, die von Funktionen auf dem Stapel verwendet werden können.
Grundsätzlich ist JavaScript Single-Threaded und führt jeweils eine Funktion aus. Diese einzelne Funktion wird auf dem Stapel platziert. Die Funktion kann auch andere verschachtelte Funktionen enthalten, die weiter oben im Stapel platziert werden. Der Stack folgt dem LIFO-Prinzip, d. h. die zuletzt aufgerufene verschachtelte Funktion wird zuerst ausgeführt.
Asynchrone Aufgaben wie API-Anfragen oder Timer werden zur späteren Ausführung zur Warteschlange hinzugefügt. Die JavaScript-Engine beginnt mit der Ausführung von Aufgaben in der Warteschlange, wenn sie inaktiv ist.
Betrachten Sie das folgende Beispiel:
function helloWorld() { console.log("Hello, World!"); } function helloPerson(name) { console.log(`Hello, ${name}!`); } function helloTeam() { console.log("Hello, Team!"); helloPerson("Monty"); } function byeWorld() { console.log("Bye, World!"); } helloWorld(); helloTeam(); byeWorld(); /* Outputs: Hello, World! Hello, Team! Hello, Monty! Bye, World! */
Sehen wir uns an, wie der Stapel und die Warteschlange aussehen würden, wenn wir den obigen Code ausführen würden.
Rufen Sie die Funktion helloWorld()
auf und legen Sie sie auf den Stapel. Es protokolliert, dass helloWorld()
函数并将其放入堆栈中。它记录 Hello, World! 完成其执行,因此它被从堆栈中取出。接下来调用 helloTeam()
函数并将其放入堆栈中。在执行过程中,我们记录 Hello, Team! 并调用 helloPerson()
。 helloTeam()
的执行还没有完成,所以它停留在堆栈上,而 helloPerson()
Hello, World!
helloTeam()
aufgerufen und auf dem Stapel abgelegt. Während der Ausführung protokollieren wir „Hallo, Team!“ und rufen helloPerson()
auf. Die Ausführung von helloTeam()
ist noch nicht abgeschlossen, daher bleibt es auf dem Stapel und helloPerson()
wird darüber platziert.
helloPerson()
现在执行。这会将 Hello, Monty! 记录到控制台,从而完成其执行,并且 helloPerson()
将从堆栈中取出。之后 helloTeam()
函数就会出栈,我们最终到达 byeWorld()
Das Last-In-First-Out-Prinzip sieht vor, dass helloPerson()
jetzt ausgeführt wird. Dadurch wird Hello, Monty! in der Konsole protokolliert, wodurch die Ausführung abgeschlossen wird und helloPerson()
vom Stapel entfernt wird. Dann wird die Funktion helloTeam()
vom Stapel entfernt und wir erreichen schließlich byeWorld()
. Es wird
verschwindet dann vom Stapel.
Die Warteschlange ist immer leer.
Betrachten Sie nun eine leichte Variation des obigen Codes: setTimeout()
function helloWorld() { console.log("Hello, World!"); } function helloPerson(name) { console.log(`Hello, ${name}!`); } function helloTeam() { console.log("Hello, Team!"); setTimeout(() => { helloPerson("Monty"); }, 0); } function byeWorld() { console.log("Bye, World!"); } helloWorld(); helloTeam(); byeWorld(); /* Outputs: Hello, World! Hello, Team! Bye, World! Hello, Monty! */
ausgegeben wird. Wenn Sie verstehen, wie die Ereignisschleife funktioniert, werden Sie verstehen, warum dies nicht geschieht. helloTeam()
入栈时,遇到setTimeout()
方法。但是,setTimeout()
中对 helloPerson()
helloTeam()
auf den Stapel geschoben wird, wird die -Methode angetroffen. Der Aufruf von helloPerson()
in byeWorld()
的调用完成,事件循环将检查队列中是否有任何挂起的任务,并找到对 helloPerson()
wird jedoch in die Warteschlange gestellt und ausgeführt, sobald keine Synchronisierungsaufgaben mehr auszuführen sind.
Sobald ein Anruf bei
Hallo, Monty!setTimeout()
an der Konsole protokolliert.
Halten Sie unsere Webseite reaktionsfähig
Eine interessante Funktion von JavaScript ist, dass eine Funktion so lange ausgeführt wird, bis sie abgeschlossen ist. Das heißt, solange sich die Funktion auf dem Stack befindet, kann die Ereignisschleife keine anderen Aufgaben in der Warteschlange verarbeiten oder andere Funktionen ausführen.
listPrimesInRange()
函数中,我们迭代从 start
到 end
的数字。对于每个数字,我们调用 isPrime()
函数来查看它是否是素数。 isPrime()
函数本身有一个 for
循环,该循环从 2
到 Math.sqrt(num)
Dies kann dazu führen, dass die Webseite „hängt“, da sie keine anderen Vorgänge ausführen kann, wie z. B. die Verarbeitung von Benutzereingaben oder das Vornehmen von DOM-bezogenen Änderungen. Betrachten Sie das folgende Beispiel, in dem wir die Anzahl der Primzahlen in einem bestimmten Bereich ermitteln:
function isPrime(num) { if (num <= 1) { return false; } for (let i = 2; i <= Math.sqrt(num); i++) { if (num % i === 0) { return false; } } return true; } function listPrimesInRange(start, end) { const primes = []; for (let num = start; num <= end; num++) { if (isPrime(num)) { primes.push(num); } } return primes; }
查找给定范围内的所有素数可能需要一段时间,具体取决于您使用的值。当浏览器进行此计算时,它无法执行任何其他操作。这是因为 listPrimesInRange()
函数将保留在堆栈中,浏览器将无法执行队列中的任何其他任务。
现在,看一下以下函数:
function listPrimesInRangeResponsively(start) { let next = start + 100,000; if (next > end) { next = end; } for (let num = start; num <= next; num++) { if (isPrime(num)) { primeNumbers.push(num); } if (num == next) { percentage = ((num - begin) * 100) / (end - begin); percentage = Math.floor(percentage); progress.innerText = `Progress ${percentage}%`; if (num != end) { setTimeout(() => { listPrimesInRangeResponsively(next + 1); }); } } if (num == end) { percentage = ((num - begin) * 100) / (end - begin); percentage = Math.floor(percentage); progress.innerText = `Progress ${percentage}%`; heading.innerText = `${primeNumbers.length - 1} Primes Found!`; console.log(primeNumbers); return primeNumbers; } } }
这一次,我们的函数仅在批量处理范围时尝试查找素数。它通过遍历所有数字但一次仅处理其中的 100,000 个来实现这一点。之后,它使用 setTimeout()
触发对同一函数的下一次调用。
setTimeout()
被调用而没有指定延迟时,它会立即将回调函数添加到事件队列中。
下一个调用将被放入队列中,暂时清空堆栈以处理任何其他任务。之后,JavaScript 引擎开始在下一批 100,000 个数字中查找素数。
尝试单击此页面上的计算(卡住)按钮,您可能会收到一条消息,指出该网页正在减慢您的浏览器速度,并建议您停止该脚本。 p>
另一方面,单击计算(响应式)按钮仍将使网页保持响应式。
在本教程中,我们了解了 JavaScript 中的事件循环以及它如何有效地执行同步和异步代码。事件循环使用队列来跟踪它必须执行的任务。
由于 JavaScript 不断执行函数直至完成,因此进行大量计算有时会“挂起”浏览器窗口。根据我们对事件循环的理解,我们可以重写我们的函数,以便它们批量进行计算。这允许浏览器保持窗口对用户的响应。它还使我们能够定期向用户更新我们在计算中取得的进展。
Das obige ist der detaillierte Inhalt vonDie Ereignisschleife in JavaScript verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!