--- title: Choisir la bonne approche slug: Learn/JavaScript/Asynchronous/Choosing_the_right_approach tags: - Beginner - Intervals - JavaScript - Learn - Optimize - Promises - async - asynchronous - await - requestAnimationFrame - setInterval - setTimeout - timeouts translation_of: Learn/JavaScript/Asynchronous/Choosing_the_right_approach ---
{{LearnSidebar}}
{{PreviousMenu("Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}

Pour terminer ce module, nous vous proposons une brève discussion sur les différentes techniques et fonctionnalités asynchrones abordées tout au long de ce module, en examinant laquelle de ces techniques est la plus pertinente en fonction de la situation ainsi que des recommandations et des rappels des pièges courants le cas échéant.

Prérequis : Connaissances informatiques de base, une compréhension raisonnable des principes fondamentaux de JavaScript.
Objectif : Être capable de faire un choix judicieux quant à l'utilisation de différentes techniques de programmation asynchrone.

Fonctions de rappels (callbacks) asynchrones

Généralement trouvé dans les API à l'ancienne, une fonction de rappel (ou callback en anglais) implique qu'une fonction soit passée en paramètre à une autre fonction, qui est ensuite invoquée lorsqu'une opération asynchrone est terminée afin réaliser une opération avec le résultat. C'est la méthode qui précédait l'arrivée des promesses : elle n'est pas aussi efficace ou flexible. Ne l'utilisez que si nécessaire.

Utile pour…
Opération unique retardée Opération répétée Opérations séquentielles multiples Opérations simultanées multiples
Non Oui (rappels récursifs) Oui (rappels imbriqués) Non

Exemple de code

Un exemple qui charge une ressource via l'API XMLHttpRequest (l'exécuter en direct, et voir la source) :

function loadAsset(url, type, callback) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.responseType = type;

  xhr.onload = function() {
    callback(xhr.response);
  };

  xhr.send();
}

function displayImage(blob) {
  let objectURL = URL.createObjectURL(blob);

  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}

loadAsset('coffee.jpg', 'blob', displayImage);

Pièges

Compatibilité des navigateurs

Très bonne prise en charge générale, bien que la prise en charge exacte dans les différentes API dépende de l'API en question. Reportez-vous à la documentation de référence de l'API que vous utilisez pour obtenir des informations plus spécifiques.

Plus d'informations

setTimeout()

setTimeout() est une méthode qui permet d'exécuter une fonction après l'écoulement d'un délai arbitraire.

Utile pour…
Opération unique retardée Opération répétée Opérations séquentielles multiples Opérations simultanées multiples
Oui Oui (délais récursifs) Oui (délais d'attente imbriqués) Non

Exemple de code

Ici, le navigateur attendra deux secondes avant d'exécuter la fonction anonyme, puis affichera le message dans la console (voir son exécution en direct, et voir le code source) :

let myGreeting = setTimeout(function() {
  console.log('Bonjour, M. Univers !');
}, 2000)

Pièges

Vous pouvez utiliser des appels récursifs à setTimeout() pour exécuter une fonction de manière répétée de façon similaire à setInterval(), en utilisant un code comme celui-ci :

let i = 1;
setTimeout(function run() {
  console.log(i);
  i++;

  setTimeout(run, 100);
}, 100);

Il existe une différence entre setTimeout() appelé récursivement et setInterval() :

Lorsque votre code a le potentiel de prendre plus de temps à s'exécuter que l'intervalle de temps que vous avez assigné, il est préférable d'utiliser setTimeout() récursivement - cela maintiendra l'intervalle de temps constant entre les exécutions, quelle que soit la durée d'exécution du code, et vous n'obtiendrez pas d'erreurs.

Compatibilité des navigateurs

{{Compat("api.WindowOrWorkerGlobalScope.setTimeout")}}

Plus d'informations

setInterval()

setInterval() est une méthode qui permet d'exécuter une fonction de façon répétée avec des intervalles de temps donnés entre chaque exécution. Cette méthode n'est pas aussi efficace que requestAnimationFrame(), mais elle permet de choisir le rythme d'exécution.

Utile pour…
Opération unique retardée Opération répétée Opérations séquentielles multiples Opérations simultanées multiples
Non Oui Non (à moins qu'elles ne soient les mêmes) Non

Exemple de code

La fonction suivante crée un nouvel objet Date(), en extrait une chaîne de temps à l'aide de toLocaleTimeString(), puis l'affiche dans l'interface utilisateur. Nous l'exécutons ensuite une fois par seconde à l'aide de setInterval(), créant l'effet d'une horloge numérique qui se met à jour une fois par seconde (voir cela en direct, et aussi voir la source) :

function displayTime() {
  let date = new Date();
  let time = date.toLocaleTimeString();
  document.getElementById('demo').textContent = time;
}

const createClock = setInterval(displayTime, 1000);

Pièges

Compatibilité des navigateurs

{{Compat("api.WindowOrWorkerGlobalScope.setInterval")}}

Plus d'informations

requestAnimationFrame()

requestAnimationFrame() est une méthode qui vous permet d'exécuter une fonction de manière répétée, et efficace, à la meilleure fréquence de rafraîchissement disponible compte tenu du navigateur/système actuel. Vous devriez, dans la mesure du possible, utiliser cette méthode au lieu de setInterval()/setTimeout() récursif, sauf si vous avez besoin d'une fréquence de rafraîchissement spécifique.

Utile pour…
Opération unique retardée Opération répétée Opérations séquentielles multiples Opérations simultanées multiples
Non Oui Non (à moins que ce soit les mêmes) Non

Exemple de code

Une toupie animée simple ; vous pouvez trouver cet exemple en direct sur GitHub (voir le code source) :

const spinner = document.querySelector('div');
let rotateCount = 0;
let startTime = null;
let rAF;

function draw(timestamp) {
  if(!startTime) {
    startTime = timestamp;
  }

  rotateCount = (timestamp - startTime) / 3;

  if(rotateCount > 359) {
    rotateCount %= 360;
  }

  spinner.style.transform = 'rotate(' + rotateCount + 'deg)';

  rAF = requestAnimationFrame(draw);
}

draw();

Pièges

Compatibilité des navigateurs

{{Compat("api.Window.requestAnimationFrame")}}

Plus d'informations

Promises (Promesses)

Les promesses sont une fonctionnalité JavaScript qui permet d'exécuter des opérations asynchrones et d'attendre qu'elles soient définitivement terminées avant d'exécuter une autre opération en fonction de son résultat. Les promesses sont la colonne vertébrale du JavaScript asynchrone moderne.

Utile pour…
Opération unique retardée Opération répétée Opérations séquentielles multiples Opérations simultanées multiples
Non Non Oui Voir Promise.all(), en dessous

Exemple de code

Le code suivant va chercher une image sur le serveur et l'affiche à l'intérieur d'un élément <img> ; voyez-le aussi en direct, et voyez aussi le code source :

fetch('coffee.jpg')
.then(response => response.blob())
.then(myBlob => {
  let objectURL = URL.createObjectURL(myBlob);
  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
})
.catch(e => {
  console.log('Il y a eu un problème avec votre opération de récupération : ' + e.message);
});

Pièges

Les chaînes de promesses peuvent être complexes et difficiles à analyser. Si vous imbriquez un certain nombre de promesses, vous pouvez vous retrouver avec des problèmes similaires à l'enfer des rappels. Par exemple :

remotedb.allDocs({
  include_docs: true,
  attachments: true
}).then(function (result) {
  let docs = result.rows;
  docs.forEach(function(element) {
    localdb.put(element.doc).then(function(response) {
      alert("Un document extrait avec un id " + element.doc._id + " et ajouté à la base de données locale.");
    }).catch(function (err) {
      if (err.name == 'conflict') {
        localdb.get(element.doc._id).then(function (resp) {
          localdb.remove(resp._id, resp._rev).then(function (resp) {
// et cetera...

Il est préférable d'utiliser la puissance de chaînage des promesses pour aller avec une structure plus plate et plus facile à analyser :

remotedb.allDocs(...).then(function (resultOfAllDocs) {
  return localdb.put(...);
}).then(function (resultOfPut) {
  return localdb.get(...);
}).then(function (resultOfGet) {
  return localdb.put(...);
}).catch(function (err) {
  console.log(err);
});

ou encore :

remotedb.allDocs(...)
.then(resultOfAllDocs => {
  return localdb.put(...);
})
.then(resultOfPut => {
  return localdb.get(...);
})
.then(resultOfGet => {
  return localdb.put(...);
})
.catch(err => console.log(err));

Cela couvre une grande partie des éléments de base. Pour un traitement beaucoup plus complet, voir l'excellent article Nous avons un problème avec les promesses (en), par Nolan Lawson.

Compatibilité des navigateurs

{{Compat("javascript.builtins.Promise")}}

Plus d'informations

Promise.all()

Une fonction JavaScript qui vous permet d'attendre que plusieurs promesses se terminent avant d'exécuter une autre opération basée sur les résultats de toutes les autres promesses.

Utile pour…
Opération unique retardée Opération répétée Opérations séquentielles multiples Opérations simultanées multiples
Non Non Non Oui

Exemple de code

L'exemple suivant va chercher plusieurs ressources sur le serveur, et utilise Promise.all() pour attendre qu'elles soient toutes disponibles avant de les afficher toutes — le voir fonctionner, et voir son code source :

function fetchAndDecode(url, type) {
  // Retourner la promesse de niveau supérieur, de sorte que le résultat
  // de l'ensemble de la chaîne est retourné hors de la fonction
  return fetch(url).then(response => {
    // Selon le type de fichier recherché, utilisez la fonction appropriée pour décoder son contenu
    if(type === 'blob') {
      return response.blob();
    } else if(type === 'text') {
      return response.text();
    }
  })
  .catch(e => {
    console.log(`Il y a eu un problème avec votre opération de récupération de la ressource "${url}" : ` + e.message);
  });
}

// Appeler la méthode fetchAndDecode() pour récupérer les images et le texte
// et stocker leurs promesses dans des variables
let coffee = fetchAndDecode('coffee.jpg', 'blob');
let tea = fetchAndDecode('tea.jpg', 'blob');
let description = fetchAndDecode('description.txt', 'text');

// Utiliser Promise.all() pour exécuter le code uniquement lorsque
// les trois appels de fonction ont été résolus
Promise.all([coffee, tea, description]).then(values => {
  console.log(values);
  // Stocker chaque valeur retournée par les promesses dans des variables séparées ;
  // créer des URL d'objets à partir des blobs.
  let objectURL1 = URL.createObjectURL(values[0]);
  let objectURL2 = URL.createObjectURL(values[1]);
  let descText = values[2];

  // Afficher les images dans les éléments <img>
  let image1 = document.createElement('img');
  let image2 = document.createElement('img');
  image1.src = objectURL1;
  image2.src = objectURL2;
  document.body.appendChild(image1);
  document.body.appendChild(image2);

  // Afficher le texte d'un paragraphe
  let para = document.createElement('p');
  para.textContent = descText;
  document.body.appendChild(para);
});

Pièges

Compatibilité des navigateurs

{{Compat("javascript.builtins.Promise.all")}}

Plus d'informations

async/await

Un outil syntaxique construit sur les promesses qui vous permet d'exécuter des opérations asynchrones en utilisant une syntaxe qui ressemble plus à l'écriture de code de rappel synchrone.

Utile pour…
Opération unique retardée Opération répétée Opérations séquentielles multiples Opérations simultanées multiples
Non Non Oui Oui (en combinaison avec Promise.all())

Exemple de code

L'exemple suivant est un remaniement de l'exemple simple de promesse que nous avons vu précédemment, qui récupère et affiche une image, écrit à l'aide d'async/await (voir en direct, et voir le code source) :

async function myFetch() {
  let response = await fetch('coffee.jpg');
  let myBlob = await response.blob();

  let objectURL = URL.createObjectURL(myBlob);
  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
}

myFetch();

Pièges

Compatibilité des navigateurs

{{Compat("javascript.statements.async_function")}}

Plus d'informations

{{PreviousMenu("Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}

Dans ce module