Programmation asynchrone JavaScript et rappels

JavaScript est synchrone par défaut et est à thread unique. Cela signifie que le code ne peut pas créer de nouveaux threads et s'exécuter en parallèle. Découvrez ce que signifie le code asynchrone et à quoi il ressemble

Asynchronicité dans les langages de programmation

Les ordinateurs sont asynchrones de par leur conception.

Asynchrone signifie que les choses peuvent se produire indépendamment du déroulement du programme principal.

Dans les ordinateurs consommateurs actuels, chaque programme s'exécute pendant une plage horaire spécifique, puis il arrête son exécution pour permettre à un autre programme de continuer son exécution. Cette chose fonctionne dans un cycle si rapide qu'il est impossible de le remarquer, et nous pensons que nos ordinateurs exécutent de nombreux programmes simultanément, mais c'est une illusion (sauf sur les machines multiprocesseurs).

Programmes utilisés en interneinterrompt, un signal émis vers le processeur pour attirer l'attention du système.

Je n'entrerai pas dans les détails internes de cela, mais gardez simplement à l'esprit qu'il est normal que les programmes soient asynchrones et interrompent leur exécution jusqu'à ce qu'ils aient besoin d'attention, et que l'ordinateur puisse exécuter d'autres choses entre-temps. Lorsqu'un programme attend une réponse du réseau, il ne peut pas arrêter le processeur tant que la requête n'est pas terminée.

Normalement, les langages de programmation sont synchrones, et certains fournissent un moyen de gérer l'asynchronicité, dans le langage ou via des bibliothèques. C, Java, C #, PHP, Go, Ruby, Swift, Python, ils sont tous synchrones par défaut. Certains d'entre eux gèrent l'async en utilisant des threads, engendrant un nouveau processus.

JavaScript

JavaScript estsynchronepar défaut et est à thread unique. Cela signifie que le code ne peut pas créer de nouveaux threads et s'exécuter en parallèle.

Les lignes de code sont exécutées en série, l'une après l'autre, par exemple:

const a = 1
const b = 2
const c = a * b
console.log(c)
doSomething()

Mais JavaScript est né à l'intérieur du navigateur, son travail principal, au début, était de répondre aux actions des utilisateurs, commeonClick,onMouseOver,onChange,onSubmitetc. Comment pourrait-il faire cela avec un modèle de programmation synchrone?

La réponse était dans son environnement. Lele navigateurfournit un moyen de le faire en fournissant un ensemble d'API capables de gérer ce type de fonctionnalité.

Plus récemment, Node.js a introduit un environnement d'E / S non bloquant pour étendre ce concept à l'accès aux fichiers, aux appels réseau, etc.

Rappels

Vous ne pouvez pas savoir quand un utilisateur cliquera sur un bouton, alors ce que vous faites, c'est que vousdéfinir un gestionnaire d'événements pour l'événement click. Ce gestionnaire d'événements accepte une fonction, qui sera appelée lorsque l'événement est déclenché:

document.getElementById('button').addEventListener('click', () => {
  //item clicked
})

C'est le soi-disantrappeler.

Un rappel est une fonction simple qui est passée en tant que valeur à une autre fonction et ne sera exécutée que lorsque l'événement se produit. Nous pouvons le faire car JavaScript a des fonctions de première classe, qui peuvent être affectées à des variables et transmises à d'autres fonctions (appeléesfonctions d'ordre supérieur)

Il est courant d'encapsuler tout votre code client dans unloadécouteur d'événement sur lewindowobject, qui exécute la fonction de rappel uniquement lorsque la page est prête:

window.addEventListener('load', () => {
  //window loaded
  //do what you want
})

Les rappels sont utilisés partout, pas seulement dans les événements DOM.

Un exemple courant est l'utilisation de minuteries:

setTimeout(() => {
  // runs after 2 seconds
}, 2000)

Les requêtes XHR acceptent également un rappel, dans cet exemple en affectant une fonction à une propriété qui sera appelée lorsqu'un événement particulier se produit (dans ce cas, l'état de la requête change):

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
  if (xhr.readyState === 4) {
    xhr.status === 200 ? console.log(xhr.responseText) : console.error('error')
  }
}
xhr.open('GET', 'https://yoursite.com')
xhr.send()

Gestion des erreurs dans les rappels

Comment gérez-vous les erreurs avec les rappels? Une stratégie très courante consiste à utiliser ce que Node.js a adopté: le premier paramètre de toute fonction de rappel est l'objet d'erreur:rappels d'erreur en premier

S'il n'y a pas d'erreur, l'objet estnull. S'il y a une erreur, il contient une description de l'erreur et d'autres informations.

fs.readFile('/file.json', (err, data) => {
  if (err !== null) {
    //handle error
    console.log(err)
    return
  }

//no errors, process data console.log(data) })

Le problème des rappels

Les rappels sont parfaits pour les cas simples!

Cependant, chaque rappel ajoute un niveau d'imbrication, et lorsque vous avez beaucoup de rappels, le code commence à se compliquer très rapidement:

window.addEventListener('load', () => {
  document.getElementById('button').addEventListener('click', () => {
    setTimeout(() => {
      items.forEach(item => {
        //your code here
      })
    }, 2000)
  })
})

C'est juste un simple code à 4 niveaux, mais j'ai vu beaucoup plus de niveaux d'imbrication et ce n'est pas amusant.

Comment pouvons-nous résoudre ce problème?

Alternatives aux rappels

À partir de ES6, JavaScript a introduit plusieurs fonctionnalités qui nous aident avec du code asynchrone qui n'implique pas l'utilisation de rappels:

Téléchargez mon gratuitManuel du débutant JavaScript


Plus de tutoriels js: