Retarder, mettre en veille, mettre en pause et attendre en JavaScript —

De nombreux langages de programmation ont un sleep
fonction qui retardera l’exécution d’un programme pendant un nombre de secondes donné. JavaScript ne dispose pas de cette fonctionnalité intégrée, mais ne vous inquiétez pas. Dans cet article, nous explorerons diverses techniques pour implémenter des retards dans votre code JavaScript, en gardant à l’esprit la nature asynchrone du langage.
Comment créer une fonction de veille en JavaScript
Pour ceux d’entre vous qui sont ici pour une solution rapide et qui ne veulent pas entrer dans les détails techniques, nous avons ce qu’il vous faut. Voici le moyen le plus simple d’ajouter une fonction de veille à votre boîte à outils JavaScript :
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
console.log('Hello');
sleep(2000).then(() => { console.log('World!'); });
Exécutez ce code et vous verrez « Bonjour » apparaître dans votre console. Puis, après une brève pause de deux secondes, « World ! suivra. C’est un moyen simple et efficace d’introduire un délai sans transpirer.
Si c’est tout ce que vous êtes venu chercher, fantastique ! Mais si vous êtes curieux de connaître le « pourquoi » et le « comment », il y a encore beaucoup à apprendre. Il existe des nuances et des subtilités dans la gestion du temps en JavaScript qui pourraient vous être utiles. Alors continuez à lire pour en savoir plus !
Comprendre le modèle d’exécution de JavaScript
Maintenant que nous avons une solution rapide à notre actif, examinons les mécanismes du modèle d’exécution de JavaScript. Comprendre cela est crucial pour gérer efficacement le temps et les opérations asynchrones dans votre code.
Considérez le code Ruby suivant :
require 'net/http'
require 'json'
url = 'https://api.github.com/users/jameshibbard'
uri = URI(url)
response = JSON.parse(Net::HTTP.get(uri))
puts response['public_repos']
puts 'Hello!'
Comme on pouvait s’y attendre, ce code demande à l’API GitHub de récupérer mes données utilisateur. Il analyse ensuite la réponse, affiche le nombre de dépôts publics attribués à mon compte GitHub et affiche enfin « Bonjour ! » à l’écran. L’exécution va de haut en bas.
Comparez cela avec la version JavaScript équivalente :
fetch('https://api.github.com/users/jameshibbard')
.then(res => res.json())
.then(json => console.log(json.public_repos));
console.log('Hello!');
Si vous exécutez ce code, il affichera « Bonjour ! » à l’écran, alors le nombre de dépôts publics attribués à mon compte GitHub.
En effet, la récupération de données à partir d’une API est une opération asynchrone en JavaScript. L’interpréteur JavaScript rencontrera le fetch
commander et expédier la demande. Ce sera pas, cependant, attendez que la demande soit terminée. Au contraire, il continuera son chemin et affichera « Bonjour ! » à la console, puis lorsque la requête reviendra quelques centaines de millisecondes plus tard, elle affichera le nombre de dépôts.
Si tout cela est nouveau pour vous, vous devriez regarder cette excellente conférence : De toute façon, qu’est-ce que c’est que la boucle d’événements ?
Comment utiliser correctement SetTimeout en JavaScript
Maintenant que nous comprenons mieux le modèle d’exécution de JavaScript, voyons comment JavaScript gère les retards et le code asynchrone.
La manière standard de créer un délai en JavaScript consiste à utiliser son setTimeout
méthode. Par exemple:
console.log('Hello');
setTimeout(() => { console.log('World!'); }, 2000);
Cela enregistrerait « Bonjour » sur la console, puis après deux secondes « Monde ! » Et dans de nombreux cas, cela suffit : faites quelque chose, puis, après un court délai, faites autre chose. Trié !
Mais malheureusement, les choses ne sont pas toujours aussi simples.
Vous pourriez penser que setTimeout
met tout le programme en pause, mais ce n’est pas le cas. C’est une fonction asynchrone, ce qui signifie que le reste de votre code n’attendra pas qu’il soit terminé.
Par exemple, disons que vous exécutez ceci :
console.log('Hello');
setTimeout(() => { console.log('World!'); }, 2000);
console.log('Goodbye!');
Vous verrez le résultat suivant :
Hello
Goodbye!
World!
Remarquez comment « Au revoir ! » apparaît avant « Monde ! » ? C’est parce que setTimeout
ne bloque pas l’exécution du reste du code.
Cela signifie que vous ne pouvez pas faire ceci :
console.log('Hello');
setTimeout(1000);
console.log('World');
« Bonjour » et « Monde » seront immédiatement enregistrés sur la console sans aucun délai notable entre les deux.
Vous ne pouvez pas non plus faire ceci :
for (let i = 0; i < 5; i++) {
setTimeout(() => { console.log(i); }, i * 1000);
}
Prenez une seconde pour réfléchir à ce qui pourrait se produire dans l’extrait de code ci-dessus.
Il ne le fera pas imprimez les chiffres de 0 à 4 avec un délai d’une seconde entre chacun. Au lieu de cela, vous obtiendrez en réalité cinq 4 imprimés en même temps après quatre secondes. Pourquoi? Parce que la boucle ne suspend pas l’exécution. Il n’attend pas setTimeout
à terminer avant de passer à l’itération suivante.
Donc qu’est-ce setTimeout
vraiment bon pour ? Regardons cela maintenant.
Examen de la fonction setTimeout() et meilleures pratiques
Comme vous pouvez le lire notre setTimeout
Didacticielle JavaScript natif setTimeout
la fonction appelle une fonction ou exécute un extrait de code après un délai spécifié (en millisecondes).
Cela peut être utile si, par exemple, vous souhaitez afficher une fenêtre contextuelle après qu’un visiteur ait parcouru votre page pendant un certain temps, ou si vous souhaitez un court délai avant de supprimer un effet de survol d’un élément (au cas où l’utilisateur accidentellement souris).
Le setTimeout
La méthode accepte une référence à une fonction comme premier argument.
Cela peut être le nom d’une fonction :
function greet(){
alert('Howdy!');
}
setTimeout(greet, 2000);
Il peut s’agir d’une variable qui fait référence à une fonction (une expression de fonction) :
const greet = function(){
alert('Howdy!');
};
setTimeout(greet, 2000);
Ou bien il peut s’agir d’une fonction anonyme (dans ce cas, une fonction flèche) :
setTimeout(() => { alert('Howdy!'); }, 2000);
Il est également possible de passer setTimeout
une chaîne de code à exécuter :
setTimeout('alert('Howdy!');', 2000);
Cependant, cette méthode n’est pas recommandée car :
- c’est difficile à lire (et donc difficile à maintenir et/ou déboguer)
- il utilise un implicite
eval
ce qui constitue un risque potentiel pour la sécurité - c’est plus lent que les alternatives, car il doit invoquer l’interpréteur JS
Comme mentionné, setTimeout
est idéal pour déclencher une action ponctuelle après un délai, mais il est également possible d’utiliser setTimeout
(ou son cousin setInterval
) pour faire attendre JavaScript jusqu’à ce qu’une condition soit remplie. Par exemple, voici comment vous pourriez utiliser setTimeout
attendre qu’un certain élément apparaisse sur une page Web :
function pollDOM () {
const el = document.querySelector('my-element');
if (el.length) {
} else {
setTimeout(pollDOM, 300);
}
}
pollDOM();
Cela suppose que l’élément apparaîtra à un moment donné. Si vous n’êtes pas sûr que ce soit le cas, vous devrez envisager d’annuler la minuterie (en utilisant clearTimeout
ou clearInterval
).
Augmentation des délais d’attente comme alternative à la fonction veille en JavaScript
Parfois, vous pourriez avoir envie d’introduire des retards dans une séquence d’opérations. Bien que vous puissiez utiliser diverses méthodes pour émuler une fonction de veille, il existe une autre approche qui est souvent négligée : l’augmentation progressive des délais d’attente.
L’idée est simple : au lieu de mettre en pause tout le thread d’exécution, vous incrémentez le délai pour chaque opération suivante en utilisant setTimeout
. Cela vous permet de créer une séquence d’actions différées sans bloquer le navigateur ni compromettre l’expérience utilisateur.
Voici un exemple rapide pour illustrer :
let delay = 1000;
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(`This is message ${i + 1}`);
}, delay);
delay += 1000;
}
Dans cet exemple, le premier message apparaîtra après 1 seconde, le deuxième après 2 secondes, et ainsi de suite, jusqu’au cinquième message après 5 secondes.
Les avantages de cette méthode sont qu’elle est non bloquante, facile à mettre en œuvre et ne nécessite pas de connaissance des promesses ou des promesses. async/await
. Cependant, il ne convient pas aux opérations asynchrones complexes qui nécessitent un timing précis ou une gestion des erreurs.
Contrôle de flux en JavaScript moderne
Il arrive souvent, lors de l’écriture de JavaScript, que nous devions attendre que quelque chose se produise (par exemple, que des données soient récupérées à partir d’une API), puis faire quelque chose en réponse (comme mettre à jour l’interface utilisateur pour afficher les données).
L’exemple ci-dessus utilise une fonction de rappel anonyme à cet effet, mais si vous devez attendre que plusieurs choses se produisent, la syntaxe devient rapidement assez compliquée et vous vous retrouvez dans rappel en enfer.
Heureusement, le langage a considérablement évolué ces dernières années et nous propose désormais de nouvelles constructions pour éviter cela.
Par exemple, en utilisant attente asynchrone nous pouvons réécrire le code initial pour récupérer les informations de l’API GitHub :
(async () => {
const res = await fetch(`https://api.github.com/users/jameshibbard`);
const json = await res.json();
console.log(json.public_repos);
console.log('Hello!');
})();
Désormais, le code s’exécute de haut en bas. L’interpréteur JavaScript attend que la requête réseau soit terminée et que le nombre de dépôts publics soit enregistré en premier, puis le message « Bonjour ! » message.
Si c’est plutôt le genre de chose que vous essayez d’accomplir, je vous encourage à lire notre article Contrôle de flux dans JS moderne : rappels des promesses d’asynchrone/d’attente.
Mettre en veille le JavaScript natif
Si vous êtes toujours avec moi, alors je suppose que vous êtes plutôt déterminé à bloquer ce thread d’exécution et à faire attendre JavaScript.
Voici comment procéder :
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
console.log('Hello');
sleep(2000);
console.log('World!');
Comme prévu, cela enregistrera « Bonjour », fera une pause de deux secondes, puis enregistrera « Monde ! »
Cela fonctionne en utilisant le Date.maintenant méthode pour obtenir le nombre de millisecondes écoulées depuis le 1er janvier 1970 et attribuer cette valeur à un date
variable. Il crée alors un vide currentDate
variable, avant de saisir une do ... while
boucle. Dans la boucle, il récupère à plusieurs reprises le nombre de millisecondes écoulées depuis le 1er janvier 1970 et attribue la valeur à la valeur déclarée précédemment. currentDate
variable. La boucle continuera jusqu’à ce que la différence entre date
et currentDate
est inférieur au délai souhaité en millisecondes.
Travail terminé, n’est-ce pas ? Enfin, pas tout à fait…
Comment écrire une meilleure fonction de sommeil en JavaScript
Peut-être que ce code fait exactement ce que vous espérez, mais sachez qu’il présente un gros inconvénient : la boucle bloquera le thread d’exécution de JavaScript et garantira que personne ne pourra interagir avec votre programme jusqu’à ce qu’il soit terminé. Si vous avez besoin d’un délai important, il est possible que cela fasse même planter complètement les choses.
Alors que faire?
Eh bien, il est également possible de combiner les techniques apprises plus tôt dans l’article pour créer une méthode de sommeil moins intrusive :
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
console.log('Hello');
sleep(2000).then(() => { console.log('World!'); });
Ce code enregistrera « Bonjour », attendra deux secondes, puis enregistrera « Monde ! » Sous le capot, nous utilisons le setTimeout
méthode pour résoudre un promesse après un nombre donné de millisecondes.
Notez que nous devons utiliser un then
rappel pour vous assurer que le deuxième message est enregistré avec un délai. Nous pouvons également enchaîner plus de fonctions de rappel sur la première :
console.log('Hello');
sleep(2000)
.then(() => { console.log('World!'); })
.then(() => {
sleep(2000)
.then(() => { console.log('Goodbye!'); })
});
Cela fonctionne, mais ça a l’air moche. Nous pouvons le embellir en utilisant async ... await
:
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function delayedGreeting() {
console.log('Hello');
await sleep(2000);
console.log('World!');
await sleep(2000);
console.log('Goodbye!');
}
delayedGreeting();
Cela semble plus joli, mais cela signifie que quel que soit le code utilisant le sleep
la fonction doit être marquée comme async
.
Bien entendu, ces deux méthodes présentent toujours l’inconvénient (ou la particularité) d’être ne le faites pas mettre en pause toute l’exécution du programme. Seule votre fonction dort :
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function delayedGreeting() {
console.log('Hello');
await sleep(2000);
console.log('World!');
}
delayedGreeting();
console.log('Goodbye!');
Le code ci-dessus enregistre les éléments suivants :
Hello
Goodbye!
World!
Meilleures pratiques pour créer une fonction de veille JavaScript
Nous avons exploré différentes manières d’introduire des retards dans JavaScript. Récapitulons maintenant quelle méthode est la mieux adaptée aux différents scénarios et laquelle vous devriez généralement éviter.
1. Plaine setTimeout
console.log('Hello');
setTimeout(() => { console.log('World!'); }, 2000);
- 👍 Avantages: Simple à comprendre, non bloquant.
- 👎 Les inconvénients: Offre un contrôle limité sur les opérations asynchrones.
- 📝 Quand utiliser: Idéal pour les retards simples et ponctuels ou les interrogations de base.
2. Incrémentiel setTimeout
setTimeout(() => { console.log('Hello'); }, 1000);
setTimeout(() => { console.log('World!'); }, 2000);
- 👍 Avantages: Non bloquant, facile à mettre en œuvre et ne nécessite pas de connaissance des promesses ou d’async/wait.
- 👎 Les inconvénients: Ne convient pas aux opérations asynchrones complexes. Aucune gestion des erreurs.
- 📝 Quand utiliser: Utile pour les séquences simples avec un délai entre chaque étape.
3. Bloquer la boucle d’événements avec une boucle
console.log('Hello');
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < 2000);
console.log('World!');
- 👍 Avantages: Imite le comportement de sommeil traditionnel.
- 👎 Les inconvénients: bloque l’intégralité du thread, peut geler l’interface utilisateur ou planter le programme.
- ⚠️ Fortement déconseillé: N’utilisez cette méthode que si vous devez absolument interrompre l’exécution et que vous êtes conscient des risques.
4. Utiliser des promesses avec setTimeout
const sleep = function(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
console.log('Hello');
sleep(2000).then(() => { console.log('World!'); });
- 👍 Avantages: Non bloquant, plus de contrôle sur les opérations asynchrones.
- 👎 Les inconvénients: Nécessite une compréhension des promesses. Les chaînes de promesses plus longues peuvent devenir un peu compliquées.
- 📝 Quand utiliser: Lorsque vous avez besoin de plus de contrôle sur le timing et les opérations asynchrones.
5. Utilisation async/await
avec des promesses
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function delayedGreeting() {
console.log('Hello');
await sleep(2000);
console.log('World!');
await sleep(2000);
console.log('Goodbye!');
}
delayedGreeting();
- 👍 Avantages: Syntaxe propre, facile à lire, non bloquante.
- 👎 Les inconvénients: Nécessite une compréhension de
async/await
et des promesses. Nécessite une fonction « wrapping » en dehors des modules. - ✅ Fortement recommandé: Il s’agit de l’approche la plus moderne et la plus propre, en particulier lorsqu’il s’agit de plusieurs opérations asynchrones.
Conclusion
Les problèmes de timing en JavaScript sont à l’origine de nombreux maux de tête pour les développeurs, et la façon dont vous les gérez dépend de ce que vous essayez d’accomplir.
Bien qu’une fonction de mise en veille soit présente dans de nombreux autres langages, je vous encourage à adopter la nature asynchrone de JavaScript et à essayer de ne pas combattre le langage. C’est en fait plutôt sympa quand on s’y habitue.
Si vous avez des questions, rendez-vous sur le Forums SitePoint et lancez une discussion.
FAQ sur les fonctions de veille, de pause et d’attente en JavaScript
Voici quelques questions fréquemment posées sur la création de retards dans JavaScript.
Non, JavaScript n’a pas de fonction de veille intégrée, mais vous pouvez en émuler une en utilisant setTimeout
ou des promesses avec async/await
.
Oui, vous pouvez utiliser la fonction sleep en boucle si vous travaillez dans un async
fonction ou un module :async function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms));
}
for (let i = 0; i < 5; i++) { console.log(`Loop count: ${i}`); await sleep(1000); // Sleep for 1 second
}
Vous pouvez utiliser setTimeout
comme ça:setTimeout(() => { console.log('1 second passed'); }, 1000);
Ou utiliser async/await
avec des promesses :await new Promise(resolve => setTimeout(resolve, 1000)) .then(() => { console.log('1 second passed'); });
Semblable à dormir pendant 1 seconde, changez simplement la durée à 5 000 millisecondes :setTimeout(() => { console.log('5 seconds passed'); }, 5000);
Ou utiliser async/await
avec des promesses :await new Promise(resolve => setTimeout(resolve, 5000))
.then(() => { console.log('5 seconds passed'); });
Vous pouvez créer un délai d’attente à l’aide du setTimeout
fonction:setTimeout(() => { /* Your code here */ }, timeInMilliseconds);
wait()
faire en JavaScript ?Il n’y a pas de natif wait()
fonction en JavaScript. Cependant, vous pouvez créer un effet similaire en utilisant async/await
avec des promesses ou setTimeout
.
Source link