setTimeout()
============

La méthode globale **`setTimeout()`** permet de définir un minuteur qui exécute une fonction ou un code donné après la fin du délai indiqué.

Syntaxe
-------


```js
setTimeout(code);
setTimeout(code, delay);

setTimeout(functionRef);
setTimeout(functionRef, delay);
setTimeout(functionRef, delay, param1);
setTimeout(functionRef, delay, param1, param2);
setTimeout(functionRef, delay, param1, param2, /\* ... ,\*/ paramN);
```

### Paramètres

`functionRef`

Une fonction à exécuter lorsque le délai du minuteur est expiré.

`code`

Une syntaxe alternative qui permet d'inclure une chaîne de caractères plutôt qu'une fonction. Le code contenu est compilé et exécuté lorsque le minuteur expire. Cette syntaxe est **déconseillée** pour les mêmes raisons qu'`eval()` et représente un risque de sécurité.

`delay` Facultatif

La durée, exprimée en millisecondes, que le minuteur devrait attendre avant l'exécution de la fonction indiquée. Si ce paramètre est absent, c'est 0 qui est utilisé comme valeur par défaut, indiquant que la fonction doit être exécutée au plus vite, c'est-à-dire au prochain cycle d'évènements.

Que le paramètre soit fourni ou non, la durée attendue avant l'exécution peut être plus longue que le nombre de millisecondes exprimées, voir les raisons pour lesquelles la durée effective est plus longue ci-après.

On notera également que si la valeur n'est pas un nombre, une conversion implicite est effectuée pour transformer la valeur en un nombre. Voir un exemple de conversion de valeur non-numérique pour la durée ci-après.

`param1`, …, `paramN` Facultatif

Des arguments additionnels qui sont passés à la fonction donnée par l'argument `function`.

### Valeur de retour

Un entier positif `timeoutID` qui identifie le minuteur créé par l'appel à `setTimeout()`. Cette valeur peut être passée à `clearTimeout()` (en-US) pour annuler le minuteur.

Une même valeur de `timeoutID` ne sera jamais réutilisée par un appel ultérieur à `setTimeout()` ou `setInterval()` sur le même objet (que ce soit une fenêtre ou un *worker*). Toutefois, des objets différents utilisent des ensembles distincts d'identifiants.

Description
-----------

Les minuteurs sont annulés grâce à la fonction `clearTimeout()` (en-US).

Pour appeler une fonction de façon répétée (toutes les *N* millisecondes), on utilisera plutôt `setInterval()` (en-US).

### Conversion de valeur non-numérique pour la durée

Lorsque `setTimeout()` est appelée avec une valeur du paramètre `delay` qui n'est pas un nombre, une conversion de type sera effectuée implicitement pour convertir la valeur en nombre. Ainsi, dans l'exemple qui suit, le code fait l'erreur d'utiliser la valeur `"1000"` qui est une chaîne de caractères, plutôt que le nombre `1000`, mais cela fonctionne, car la chaîne est transformée implicitement dans la valeur numérique `1000`, et la fonction est donc exécutée après 1 seconde.


```js
setTimeout(() => {
  console.log("Retardée d'une seconde.");
}, "1000");
```


Toutefois, dans de nombreux cas, la conversion implicite peut mener à des résultats inattendus voire surprenants. Par exemple, lorsque le code qui suit est exécuté, la chaîne de caractères `"1 seconde"` est en fait transformée dans le nombre `0`, et le code est donc exécuté immédiatement.


```js
setTimeout(() => {
  console.log("Retardée d'une seconde.");
}, "1 seconde");
```


Aussi, on veillera à ne pas utiliser de chaîne de caractères pour le paramètre `delay` et à utiliser uniquement des nombres :


```js
setTimeout(() => {
  console.log("Retardée d'une seconde.");
}, 1000);
```

### Fonctionnement avec les fonctions asynchrones

`setTimeout()` est une fonction asynchrone, ce qui signifie que la fonction passée en argument ne bloquera pas l'exécution des autres fonctions de la pile d'appels. Autrement dit, on ne peut pas utiliser `setTimeout()` afin de créer une pause avant que la prochaine fonction de la pile soit déclenchée.

Prenons cet exemple :


```js
setTimeout(() => {
  console.log("Voici le premier message");
}, 5000);
setTimeout(() => {
  console.log("Voici le second message");
}, 3000);
setTimeout(() => {
  console.log("Voici le troisième message");
}, 1000);

// Produira ceci dans la console&nbsp;:

// Voici le troisième message
// Voici le second message
// Voici le premier message
```


On voit ici que la première fonction ne crée pas une pause de 5 secondes que la deuxième fonction soit appelée. La première fonction est appelée par le moteur dès lors que 5 secondes sont écoulées. Pendant ce délai, le reste des instructions continue de s'exécuter et la deuxième fonction est appelée par le moteur dès que 3 secondes se sont écoulées. Juste après, il en va de même après la troisième fonction. Le délai de 1 seconde étant écoulé d'abord, c'est la troisième fonction qui est exécutée d'abord, puis la seconde et enfin la première.

Pour créer une séquence d'opérations où une fonction se déclenche uniquement après qu'une autre fonction ait terminé, on utilisera plutôt les promesses.

### Le problème de `this`

Lorsqu'on passe une méthode à `setTimeout()`, elle est appelée avec une valeur de `this` qui peut être différente de celle attendue. Ce problème général est détaillé dans la référence JavaScript.

Le code exécuté par `setTimeout()` est appelé par un contexte d'exécution séparé de la fonction depuis laquelle `setTimeout()` a été appelé. Les règles usuelles pour la définition du mot-clé `this` s'appliquent et si `this` n'est pas défini lors de l'appel ou avec `bind()`, sa valeur par défaut sera l'objet `window` (ou `global`). Il ne s'agira pas de la même valeur de `this` qu'au sein de la fonction qui a appelé `setTimeout()`.

Prenons l'exemple suivant :


```js
const monTableau = ["zéro", "un", "deux"];
monTableau.maMethode = function (sPropriete) {
  console.log(arguments.length > 0 ? this[sPropriete] : this);
};

monTableau.maMethode(); // affiche "zéro,un,deux"
monTableau.maMethode(1); // affiche "un"
```


Cela fonctionne, car, lorsque `maMethode` est appelée, `this` vaut `monTableau` et, au sein de la fonction, `this[sPropriete]` est donc équivalent à `monTableau[sPropriete]`. Toutefois, si on écrit ceci :


```js
setTimeout(monTableau.maMethode, 1.0 \* 1000); // affiche "[object Window]" après 1 seconde
setTimeout(monTableau.maMethode, 1.5 \* 1000, "1"); // affiche "undefined" après 1.5 secondes
```


La fonction `monTableau.maMethode` est passée à `setTimeout`, et lorsqu'elle est appelée, la valeur de `this` n'est pas définie et vaut `window` par défaut.

Il n'y a pas d'argument `thisArg` pour `setTimeout` (comme on peut en voir pour `forEach()` et `reduce()`. Comme indiqué après, utiliser `call()` ne fonctionne pas non plus.


```js
setTimeout.call(monTableau, monTableau.maMethode, 2.0 \* 1000); // erreur
setTimeout.call(monTableau, monTableau.maMethode, 2.5 \* 1000, 2); // erreur également
```


#### Solutions

##### Utiliser une fonction englobante

Une méthode pour résoudre ce problème consiste à englober la méthode dans une fonction afin que `this` ait la valeur attendue :


```js
setTimeout(function () {
  monTableau.maMethode();
}, 2.0 \* 1000);
// affiche "zéro,un,deux" après 2 secondes
setTimeout(function () {
  monTableau.maMethode("1");
}, 2.5 \* 1000);
// affiche "un" après 2.5 secondes
```


La fonction englobante peut être une fonction fléchée :


```js
setTimeout(() => {
  monTableau.maMethode();
}, 2.0 \* 1000);
// affiche "zéro,un,deux" après 2 secondes
setTimeout(() => {
  monTableau.maMethode("1");
}, 2.5 \* 1000);
// affiche "un" après 2.5 secondes
```


##### Utiliser `bind()`

On peut aussi utiliser la fonction `bind()` afin de fixer la valeur de `this` pour tous les appels à une fonction donnée :


```js
const monTableau = ["zéro", "un", "deux"];
const maMethodeLiee = function (sPropriete) {
  console.log(arguments.length > 0 ? this[sPropriete] : this);
}.bind(monTableau);

maMethodeLiee();
// affiche "zéro,un,deux" car 'this' est lié à monTableau
// dans la fonction
maMethodeLiee(1);
// affiche "un"
setTimeout(maMethodeLiee, 1.0 \* 1000);
// Affiche "zéro,un,deux" après 1 seconde grâce à la liaison
setTimeout(maMethodeLiee, 1.5 \* 1000, "1");
// Affiche "un" après 1.5 secondes
```

### Passer une chaîne de caractères pour les instructions à exécuter

Lorsqu'on passe une chaîne de caractères plutôt qu'une fonction à `setTimeout()`, cela expose aux mêmes problèmes que d'utiliser `eval()`.


```js
// À ne pas faire
setTimeout("console.log('Hello World!');", 500);
```



```js
// On privilégiera cette forme
setTimeout(function () {
  console.log("Hello World!");
}, 500);
```


Une chaîne de caractères passée à `setTimeout()` est évaluée dans le contexte global et les symboles locaux au contexte où `setTimeout()` a été appelée ne seront plus disponibles lorsque la chaîne de caractères sera évaluée comme du code.

### Raisons pour lesquelles la durée effective est plus longue

Plusieurs raisons peuvent expliquer que la durée effective d'un minuteur soit plus longue que celle attendue. Dans cette section, nous couvrirons les raisons les plus communes.

#### Minuteurs imbriqués

Comme indiqué dans le standard HTML, les navigateurs appliqueront un délai minimum de 4 millisecondes lorsqu'un appel imbriqué à `setTimeout` a été planifié 5 fois.

On peut observer ce comportement dans l'exemple qui suit, dans lequel on imbrique un appel à `setTimeout()` avec une durée spécifiée à `0` milliseconde. Ensuite, on affiche la durée effective lorsque le gestionnaire est appelé. On peut voir que, pour les 4 premières fois, la durée effective est de 0 milliseconde environ et qu'après, elle passe à environ 4 millisecondes :


```html
<button id="run">Exécuter</button>
<pre>Précédent    courant    durée</pre>
<div id="log"></div>
```



```js
let last = 0;
let iterations = 10;

function timeout() {
  // Enregistrer l'instant de l'appel
  logline(new Date().getMilliseconds());

  // Tant qu'on n'a pas fini, planifier le prochain appel
  if (iterations-- > 0) {
    setTimeout(timeout, 0);
  }
}

function run() {
  // Nettoyer le journal
  const log = document.querySelector("#log");
  while (log.lastElementChild) {
    log.removeChild(log.lastElementChild);
  }

  // Initialiser le nombre d'itérations et l'horodatage
  // de départ
  iterations = 10;
  last = new Date().getMilliseconds();

  // Démarrer le minuteur
  setTimeout(timeout, 0);
}

function pad(number) {
  return number.toString().padStart(3, "0");
}

function logline(now) {
  // Afficher le dernier horodatage, le nouveau, et la
  // différence
  const newLine = document.createElement("pre");
  newLine.textContent = `${pad(last)} ${pad(now)} ${
 now - last
 }`;
  document.getElementById("log").appendChild(newLine);
  last = now;
}

document.querySelector("#run").addEventListener("click", run);
```


#### Minuteurs dans les onglets inactifs

Afin de réduire la charge (et la consommation énergétique associée) des onglets en arrière-plan, les navigateurs appliqueront un délai minimum sur les onglets inactifs. Ceci peut ne pas s'appliquer si la page joue du son avec l'API Web Audio et `AudioContext`.

Certains aspects spécifiques de cette règle dépendent des navigateurs :

* Firefox Desktop et Chrome appliquent une durée minimale de 1 seconde pour les onglets inactifs.
* Firefox pour Android applique une durée minimale de 15 minutes pour les onglets inactifs et peut les décharger complètement.
* Firefox ne limite pas un onglet inactif si celui-ci contient un `AudioContext`.

#### Limitation des scripts de pistage

Firefox applique certaines limitations pour les scripts qu'il reconnaît comme scripts de pistage. Lors de l'exécution dans un onglet actif, le délai minimal est de 4ms. Pour les onglets en arrière-plan, ce délai passe à 10 seconds et s'applique 30 secondes après que le chargement initial du document a eu lieu.

Voir la page d'information sur la protection contre le pistage (en anglais) pour plus de détails.

#### Ordonnancement des autres tâches

Le minuteur peut également se déclencher plus tard si le système d'exploitation ou le navigateur est occupé à d'autres tâches.

Par exemple, on notera que la fonction passée en argument de `setTimeout()` ne peut pas être exécutée avant que le *thread* qui appelle `setTimeout()` ait terminé son exécution. On aura donc :


```js
function toto() {
  console.log("toto a été appelée");
}
setTimeout(toto, 0);
console.log("Après setTimeout()");
```


Affichera ce qui suit dans la console :
```

Après setTimeout()
toto a été appelée
```


En effet, même si `setTimeout()` a été appelé avec un délai à zéro, la fonction correspondante est placée dans une queue et son exécution est planifiée pour le prochain cycle disponible et pas immédiatement.

Le code en cours d'exécution doit terminer avant que les fonctions placées dans la queue puissent être exécutées. C'est pour cela qu'on a cet ordre d'exécution, qui peut ne pas être celui qu'on attendait.

#### Report des minuteurs pendant le chargement de la page

Firefox diffèrera le déclenchement des minuteurs lorsque l'onglet actuel est en train de charger. Le déclenchement est reporté jusqu'à ce que le fil d'exécution principal soit considéré comme inactif (*idle*) (à la façon de window.requestIdleCallback()), ou jusqu'à ce que l'évènement `load` soit déclenché.

### Pages d'arrière-plan des WebExtensions et minuteurs

Pour les WebExtensions, `setTimeout()` ne fonctionne pas de façon sûre. Il faut privilégier l'API `alarms` pour le développement d'extensions.

### Valeur maximale pour le délai

Les navigateurs comme Internet Explorer, Chrome, Safari, et Firefox stockent le délai sous la forme d'un entier signé sur 32 bits. Aussi, il y a un dépassement des limites lorsqu'on indique un délai supérieur à 2 147 483 647 ms (ce qui correspond à 24,8 jours), et le résultat est un minuteur qui est exécuté immédiatement.

Exemples
--------

### Définir et annuler des minuteurs

Dans l'exemple qui suit, on a deux boutons simples sur une page web qui sont reliés aux fonctions `setTimeout()` et `clearTimeout()`. Appuyer sur le premier bouton déclenchera un minuteur qui affichera un message après deux secondes et enregistrera l'identifiant du minuteur pour que celui-ci soit utilisé avec `clearTimeout()`. En appuyant sur le deuxième bouton, on peut annuler le minuteur.

#### HTML


```html
<button onclick="delayedMessage();">
  Afficher un message après deux secondes
</button>
<button onclick="clearMessage();">
  Annuler le message avant qu'il apparaisse
</button>

<div id="output"></div>
```


#### JavaScript


```js
let timeoutID;

function setOutput(outputContent) {
  document.querySelector("#output").textContent = outputContent;
}

function delayedMessage() {
  setOutput("");
  timeoutID = setTimeout(setOutput, 2 \* 1000, "C'était lent !");
}

function clearMessage() {
  clearTimeout(timeoutID);
}
```


```
#output {
  padding: 0.5rem 0;
}
```


#### Résultat

Voir aussi l'exemple `clearTimeout()` (en-US).

Spécifications
--------------

| Specification |
| --- |
| HTML Standard # dom-settimeout-dev |

Compatibilité des navigateurs
-----------------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

Voir aussi
----------

* Prothèse d'émulation de `setTimeout()` qui permet de passer des arguments à la fonction de rappel, avec la bibliothèque tierce `core-js`
* `clearTimeout()` (en-US)
* `setInterval()` (en-US)
* `Window.requestAnimationFrame()`
* `queueMicrotask()`