Vous êtes-vous déjà demandé pourquoi certains morceaux de code JavaScript semblent fonctionner dans le désordre ? La clé pour comprendre cela est la boucle d'événements.
La boucle d'événements JavaScript peut être difficile à comprendre, en particulier lorsqu'il s'agit de différents types d'opérations asynchrones. Dans cet article, nous expliquerons comment JavaScript gère le code synchrone et asynchrone, les microtâches et les macrotâches, et pourquoi certaines choses se produisent. dans un ordre précis.
JavaScript gère les opérations de deux manières principales : synchrone et asynchrone. Comprendre la différence entre eux est essentiel pour comprendre comment JavaScript gère les tâches et comment écrire du code efficace et non bloquant.
Le code synchrone est la valeur par défaut en JavaScript, ce qui signifie que chaque ligne s'exécute l'une après l'autre dans l'ordre. Par exemple :
console.log("First"); console.log("Second");
Cela affichera :
First Second
Le code asynchrone, quant à lui, permet à certaines tâches de s'exécuter en arrière-plan et de se terminer plus tard, sans bloquer le reste du code. Des fonctions comme setTimeout() ou Promise sont des exemples de code asynchrone.
Voici un exemple simple de code asynchrone utilisant setTimeout() :
console.log("First"); setTimeout(() => { console.log("Second"); }, 0); console.log("Third");
Cela affichera :
First Third Second
Il existe plusieurs façons de gérer les opérations asynchrones en JavaScript :
Exemple de code :
console.log("Start"); function asyncTask(callback) { setTimeout(() => { console.log("Async task completed"); callback(); }, 2000); } asyncTask(() => { console.log("Task finished"); }); console.log("End");
Exemple de code :
console.log("Start"); const asyncTask = new Promise((resolve) => { setTimeout(() => { console.log("Async task completed"); resolve(); }, 2000); }); asyncTask.then(() => { console.log("Task finished"); }); console.log("End");
Exemple de code :
console.log("Start"); async function asyncTask() { await new Promise((resolve) => { setTimeout(() => { console.log("Async task completed"); resolve(); }, 2000); }); console.log("Task finished"); } asyncTask(); console.log("End");
Pour mieux comprendre chacune de ces méthodes d'exécution de javascript et en quoi elles diffèrent de chacune, voici une description détaillée des différences entre plusieurs aspects des fonctions javascript.
Aspect | Synchronous Code | Asynchronous Code |
---|---|---|
Execution Order | Executes line by line in a sequential manner | Allows tasks to run in the background while other code continues to execute |
Performance | Can lead to performance issues if long-running tasks are involved | Better performance for I/O-bound operations; prevents UI freezing in browser environments |
Code Complexity | Generally simpler and easier to read | Can be more complex, especially with nested callbacks (callback hell) |
Memory Usage | May use more memory if waiting for long operations | Generally more memory-efficient for long-running tasks |
Scalability | Less scalable for applications with many concurrent operations | More scalable, especially for applications handling multiple simultaneous operations |
This comparison highlights the key differences between synchronous and asynchronous code, helping developers choose the appropriate approach based on their specific use case and performance requirements.
In JavaScript, microtasks and macrotasks are two types of tasks that are queued and executed in different parts of the event loop, which determines how JavaScript handles asynchronous operations.
Microtasks and macrotasks are both queued and executed in the event loop, but they have different priorities and execution contexts. Microtasks are processed continuously until the microtask queue is empty before moving on to the next task in the macrotask queue. Macrotasks, on the other hand, are executed after the microtask queue has been emptied and before the next event loop cycle starts.
Microtasks are tasks that need to be executed after the current operation completes but before the next event loop cycle starts. Microtasks get priority over macrotasks and are processed continuously until the microtask queue is empty before moving on to the next task in the macrotask queue.
console.log("Start"); Promise.resolve().then(() => { console.log("Microtask"); }); console.log("End");
Start End Microtask
Macrotasks are tasks that are executed after the microtask queue has been emptied and before the next event loop cycle starts. These tasks represent operations like I/O or rendering and are usually scheduled after a certain event or after a delay.
console.log("Start"); setTimeout(() => { console.log("Macrotask"); }, 0); console.log("End");
Start End Macrotask
Aspect | Microtasks | Macrotasks |
---|---|---|
Execution Timing | Executed immediately after the current script, before rendering | Executed in the next event loop iteration |
Queue Priority | Higher priority, processed before macrotasks | Lower priority, processed after all microtasks are complete |
Examples | Promises, queueMicrotask(), MutationObserver | setTimeout(), setInterval(), I/O operations, UI rendering |
Use Case | For tasks that need to be executed as soon as possible without yielding to the event loop | For tasks that can be deferred or don't require immediate execution |
La boucle d'événements est un concept fondamental en JavaScript qui permet des opérations asynchrones non bloquantes même si JavaScript est monothread. Il est chargé de gérer les rappels asynchrones et de garantir que JavaScript continue de fonctionner correctement sans être bloqué par des opérations fastidieuses.
La boucle d'événements est un mécanisme qui permet à JavaScript de gérer efficacement les opérations asynchrones. Il vérifie en permanence la pile d'appels et la file d'attente des tâches (ou file d'attente des microtâches) pour déterminer quelle fonction doit être exécutée ensuite.
Pour mieux comprendre la boucle des événements, il est important de savoir comment fonctionne JavaScript en interne. Il est important de noter que JavaScript est un langage à thread unique, ce qui signifie qu'il ne peut faire qu'une seule chose à la fois. Il n'y a qu'une seule pile d'appels, qui stocke les fonctions à exécuter. Cela rend le code synchrone simple, mais cela pose un problème pour des tâches telles que la récupération de données sur un serveur ou la définition d'un délai d'attente, qui prennent du temps. Sans la boucle d'événements, JavaScript serait bloqué en attendant ces tâches et rien d'autre ne se produirait.
La pile d'appels est l'endroit où la fonction en cours d'exécution est conservée. JavaScript ajoute et supprime des fonctions de la pile d'appels au fur et à mesure qu'il traite le code.
Lorsqu'une tâche asynchrone telle que setTimeout, fetch ou Promise est rencontrée, JavaScript délègue cette tâche aux API Web du navigateur (telles que l'API Timer, l'API réseau, etc.), qui gèrent la tâche en arrière-plan.
Une fois la tâche asynchrone terminée (par exemple, la minuterie se termine ou les données sont reçues du serveur), le rappel (la fonction pour gérer le résultat) est déplacé vers la file d'attente des tâches (ou la file d'attente des microtâches dans le cas de promesses) .
JavaScript continue d'exécuter le code synchrone. Une fois la pile d'appels vide, la boucle d'événements récupère la première tâche de la file d'attente des tâches (ou de la file d'attente des microtâches) et la place sur la pile d'appels pour exécution.
Ce processus se répète. La boucle d'événements garantit que toutes les tâches asynchrones sont traitées une fois les tâches synchrones en cours terminées.
Maintenant que nous comprenons mieux et plus clairement le fonctionnement de la boucle événementielle, examinons quelques exemples pour consolider notre compréhension.
function exampleOne() { console.log("Start"); setTimeout(() => { console.log("Timeout done"); }, 1000); Promise.resolve().then(() => { console.log("Resolved"); }); console.log("End"); } exampleOne();
Start End Resolved Timeout done
function exampleTwo() { console.log("Start"); setTimeout(() => { console.log("Timer 1"); }, 0); Promise.resolve().then(() => { console.log("Promise 1 Resolved"); setTimeout(() => { console.log("Timer 2"); }, 0); return Promise.resolve().then(() => { console.log("Promise 2 Resolved"); }); }); console.log("End"); } exampleTwo();
Start End Promise 1 Resolved Promise 2 Resolved Timer 1 Timer 2
function exampleThree() { console.log("Step 1: Synchronous"); setTimeout(() => { console.log("Step 2: Timeout 1"); }, 0); Promise.resolve().then(() => { console.log("Step 3: Promise 1 Resolved"); Promise.resolve().then(() => { console.log("Step 4: Promise 2 Resolved"); }); setTimeout(() => { console.log("Step 5: Timeout 2"); }, 0); }); setTimeout(() => { console.log( "Step 6: Immediate (using setTimeout with 0 delay as fallback)" ); }, 0); console.log("Step 7: Synchronous End"); } exampleThree();
Step 1: Synchronous Step 7: Synchronous End Step 3: Promise 1 Resolved Step 4: Promise 2 Resolved Step 2: Timeout 1 Step 6: Immediate (using setTimeout with 0 delay as fallback) Step 5: Timeout 2
In JavaScript, mastering synchronous and asynchronous operations, as well as understanding the event loop and how it handles tasks, is crucial for writing efficient and performant applications.
The examples provided progressively illustrated the interaction between synchronous code, promises, timers, and the event loop. Understanding these concepts is key to mastering asynchronous programming in JavaScript, ensuring your code runs efficiently and avoids common pitfalls such as race conditions or unexpected execution orders.
To ensure you don't miss any part of this series and to connect with me for more in-depth discussions on Software Development (Web, Server, Mobile or Scraping / Automation), push notifications, and other exciting tech topics, follow me on:
Stay tuned and happy coding ???
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!