Maison  >  Article  >  Tutoriel CMS  >  Comprendre la boucle d'événements en JavaScript

Comprendre la boucle d'événements en JavaScript

PHPz
PHPzoriginal
2023-09-03 13:17:101026parcourir

Comprendre la boucle dévénements en JavaScript

Vous savez probablement déjà que JavaScript est un langage de programmation monothread. Cela signifie que JavaScript s'exécute sur un seul thread principal dans un navigateur Web ou Node.js. S'exécuter sur un seul thread principal signifie exécuter un seul morceau de code JavaScript à la fois.

La boucle d'événements en JavaScript joue un rôle important dans la détermination de la manière dont le code est exécuté sur le thread principal. La boucle d'événements est responsable de choses telles que l'exécution du code ainsi que la collecte et le traitement des événements. Il gère également l'exécution de toutes les sous-tâches en file d'attente.

Dans ce tutoriel, vous apprendrez les bases des boucles d'événements en JavaScript.

Comment fonctionne la boucle d'événements

Afin de comprendre le fonctionnement de la boucle événementielle, vous devez comprendre trois termes importants.

Pile

Une pile d'appels est simplement une pile d'appels de fonctions qui retrace le contexte d'exécution d'une fonction. La pile suit le principe du dernier entré, premier sorti (LIFO), ce qui signifie que la fonction la plus récemment appelée sera la première à être exécutée.

File d'attente

Une file d'attente contient une série de tâches exécutées par JavaScript. Les tâches dans cette file d'attente peuvent provoquer l'appel de fonctions, qui sont ensuite placées sur la pile. Le traitement de la file d'attente ne commence que lorsque la pile est vide. Les éléments dans la file d'attente suivent le principe du premier entré, premier sorti (FIFO). Cela signifie que les tâches les plus anciennes seront terminées en premier.

Tas

Le tas est essentiellement une vaste zone de mémoire où les objets sont stockés et alloués. Son objectif principal est de stocker les données pouvant être utilisées par les fonctions de la pile.

Fondamentalement, JavaScript est monothread et exécute une fonction à la fois. Cette fonction unique est placée sur la stack. La fonction peut également contenir d'autres fonctions imbriquées, qui seront placées plus haut dans la pile. La pile suit le principe LIFO, donc la fonction imbriquée la plus récemment appelée sera exécutée en premier.

Les tâches asynchrones telles que les requêtes API ou les minuteries seront ajoutées à la file d'attente pour une exécution ultérieure. Le moteur JavaScript commence à exécuter les tâches dans la file d'attente lorsqu'il est inactif.

Considérons l'exemple suivant :

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!

*/

Voyons à quoi ressembleraient la pile et la file d'attente si nous exécutions le code ci-dessus.

Appelez la fonction helloWorld() et mettez-la sur la pile. Il enregistre que helloWorld() 函数并将其放入堆栈中。它记录 Hello, World! 完成其执行,因此它被从堆栈中取出。接下来调用 helloTeam() 函数并将其放入堆栈中。在执行过程中,我们记录 Hello, Team! 并调用 helloPerson()helloTeam() 的执行还没有完成,所以它停留在堆栈上,而 helloPerson()Hello, World!

a terminé son exécution, il a donc été retiré de la pile. Ensuite, la fonction helloTeam() est appelée et placée sur la pile. Pendant l'exécution, nous enregistrons

Bonjour, Team !helloPerson() 现在执行。这会将 Hello, Monty! 记录到控制台,从而完成其执行,并且 helloPerson() 将从堆栈中取出。之后 helloTeam() 函数就会出栈,我们最终到达 byeWorld() et appelons helloPerson(). L'exécution de helloTeam() n'est pas encore terminée, elle reste donc sur la pile et helloPerson() est placée dessus. Le principe du dernier entré, premier sorti stipule que helloPerson() est exécuté maintenant. Cela enregistrera

Hello, Monty!

sur la console, complétant ainsi son exécution et helloPerson() sera retiré de la pile. Ensuite, la fonction helloTeam() sortira de la pile, et nous atteignons enfin byeWorld(). Il enregistrera

Au revoir, monde !

disparaît ensuite de la pile.

La file d'attente est toujours vide. setTimeout() Maintenant, considérons une légère variation du code ci-dessus :

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!

*/
Le seul changement que nous avons apporté ici est d'utiliser . Cependant, le délai d'attente a été fixé à zéro. Par conséquent, nous nous attendons à ce que Bonjour, Monty !

soit affiché avant helloTeam()入栈时,遇到setTimeout()方法。但是,setTimeout() 中对 helloPerson()Bye, World !

. Si vous comprenez comment fonctionne la boucle d'événements, vous comprendrez pourquoi cela ne se produit pas.

byeWorld() 的调用完成,事件循环将检查队列中是否有任何挂起的任务,并找到对 helloPerson()Lorsque helloTeam() est poussé sur la pile, la méthode est rencontrée. Cependant, l'appel à helloPerson() dans sera mis dans la file d'attente et sera exécuté une fois qu'il n'y aura plus de tâches de synchronisation à exécuter.

Une fois qu'un appel à setTimeout() est effectué. À ce stade, il exécute la fonction et enregistre

Bonjour, Monty !

sur la console.

Cela indique que la durée d'expiration que vous fournissez à

n'est pas une durée garantie pour l'exécution du rappel. Il s'agit du temps minimum d'exécution du rappel.

Gardez notre page Web réactive

listPrimesInRange() 函数中,我们迭代从 startend 的数字。对于每个数字,我们调用 isPrime() 函数来查看它是否是素数。 isPrime() 函数本身有一个 for 循环,该循环从 2Math.sqrt(num)Une fonctionnalité intéressante de JavaScript est qu'il exécutera une fonction jusqu'à ce qu'elle se termine. Cela signifie que tant que la fonction est sur la pile, la boucle d'événements ne peut traiter aucune autre tâche dans la file d'attente ni exécuter d'autres fonctions.

🎜Cela peut entraîner le « blocage » de la page Web, car elle est incapable d'effectuer d'autres opérations, telles que le traitement des entrées de l'utilisateur ou la réalisation de modifications liées au DOM. Prenons l'exemple suivant, où l'on trouve le nombre de nombres premiers dans une plage donnée : 🎜
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;
}
🎜 dans notre 🎜 pour déterminer si un nombre est premier. 🎜

查找给定范围内的所有素数可能需要一段时间,具体取决于您使用的值。当浏览器进行此计算时,它无法执行任何其他操作。这是因为 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 不断执行函数直至完成,因此进行大量计算有时会“挂起”浏览器窗口。根据我们对事件循环的理解,我们可以重写我们的函数,以便它们批量进行计算。这允许浏览器保持窗口对用户的响应。它还使我们能够定期向用户更新我们在计算中取得的进展。

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