JavaScript et le futur de l'asynchrone
Un article de David Catuhe traduit par yahiko

Le , par yahiko, Rédacteur/Modérateur

Ce qui suit est une traduction d'un article du blog de David Catuhe (Microsoft) que je remercie pour avoir donné aimablement son accord

JavaScript en a fait du chemin depuis ses premières versions, et grâce à tous les efforts réalisés par le TC39 (L'organisation en charge de la standardisation de JavaScript, ou ECMAScript pour être exact), nous avons maintenant un langage moderne qui est largement utilisé.

Une des parties d'ECMAScript qui a reçu de grandes améliorations est la programmation asynchrone. Vous pouvez en apprendre plus sur la programmation asynchrone ici si vous êtes un nouveau développeur. Heureusement, nous avons inclus ces changements dans le nouveau navigateur Edge de Windows 10. Consultez le journal des modifications ci-dessous :

https://dev.modern.ie/platform/changelog/desktop/10547/

Parmi toutes ces nouvelles fonctionnalités, nous allons nous concentrer en particulier sur les "Fonctions async ES2016" (ES2016 Async Functions), effectuer un voyage à travers ces mises à jour et voir comment ECMAScript peut améliorer votre travail au quotidien dès aujourd'hui.

Première étape : ECMAScript 5 - Callback City

ECMAScript 5 (et les versions antérieures) ne jurent que par les callbacks (fonctions auxiliaires). Pour illustrer ceci, prenons un exemple simple que vous utilisez certainement plus d'une fois par jour : l'exécution d'une requête XHR.

Code javascript : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var displayDiv = document.getElementById("displayDiv"); 
  
// Part 1 - Defining what do we want to do with the result 
var processJSON = function (json) { 
    var result = JSON.parse(json); 
  
    result.collection.forEach(function(card) { 
        var div = document.createElement("div"); 
        div.innerHTML = card.name + " cost is " + card.price; 
  
        displayDiv.appendChild(div); 
    }); 
} 
  
// Part 2 - Providing a function to display errors 
var displayError = function(error) { 
    displayDiv.innerHTML = error; 
} 
  
// Part 3 - Creating and setting up the XHR object 
var xhr = new XMLHttpRequest(); 
  
xhr.open('GET', "cards.json"); 
  
// Part 4 - Defining callbacks that XHR object will call for us 
xhr.onload = function(){ 
    if (xhr.status === 200) { 
        processJSON(xhr.response); 
    } 
} 
  
xhr.onerror = function() { 
    displayError("Unable to load RSS"); 
} 
  
// Part 5 - Starting the process 
xhr.send();

Les développeurs JavaScript chevronnés noteront à quel point cela leur semble familier puisque les callbacks pour XHR sont utilisées tout le temps ! C'est simple et relativement direct : le développeur crée une requête XHR, puis fournit le callback de l'objet XHR spécifié.

En revanche, la complexité du callback vient de l'ordre d'exécution qui est non linéaire en raison de la nature intrinsèque de la programmation asynchrone :



"L'Enfer des callbacks" peut même être pire lorsque vous utilisez un autre appel asynchrone au sein de votre propre callback.

Seconde étape : ECMAScript 6 – Promises City

ECMAScript 6 est sur une bonne dynamique d'adoption où Edge détient le meilleur score avec 88% (en mode expérimental).

Parmi de nombreuses améliorations d'importances, ECMAScript 6 standardise l'utilisation des promises (anciennement connues sous le nom de futures).

Selon MDN, une promise est un objet qui est utilisé pour les opérations déportées (deferred) et asynchrones. Une promise représente une opération qui ne s'est pas encore terminée, mais qui est prévue de l'être à l'avenir. Les promises sont une façon d'organiser les opérations asynchrones de façon à ce qu'elles apparaissent synchrones dans le code. Exactement ce dont nous avons besoin pour notre exemple avec XHR.

Les promises existent depuis un certain temps, mais la bonne nouvelle est que maintenant vous n'avez plus à utiliser une bibliothèque JavaScript puisqu'elles sont fournies dans le navigateur.

Mettons légèrement à jour notre exemple précédent pour utiliser les promises et voyons comment cela améliore la lisibilité et la maintenabilité de notre code :

Code javascript : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
var displayDiv = document.getElementById("displayDiv"); 
  
// Part 1 - Create a function that returns a promise 
function getJsonAsync(url) { 
    // Promises require two functions: one for success, one for failure 
    return new Promise(function (resolve, reject) { 
        var xhr = new XMLHttpRequest(); 
  
        xhr.open('GET', url); 
  
        xhr.onload = () => { 
            if (xhr.status === 200) { 
                // We can resolve the promise 
                resolve(xhr.response); 
            } else { 
                // It's a failure, so let's reject the promise 
                reject("Unable to load RSS"); 
            } 
        } 
  
        xhr.onerror = () => { 
            // It's a failure, so let's reject the promise 
            reject("Unable to load RSS"); 
        }; 
  
        xhr.send(); 
    }); 
} 
  
// Part 2 - The function returns a promise 
// so we can chain with a .then and a .catch 
getJsonAsync("cards.json").then(json => { 
    var result = JSON.parse(json); 
  
    result.collection.forEach(card => { 
        var div = document.createElement("div"); 
        div.innerHTML = `${card.name} cost is ${card.price}`; 
  
        displayDiv.appendChild(div); 
    }); 
}).catch(error => { 
    displayDiv.innerHTML = error; 
});

Ci-dessus, vous avez peut-être remarqué beaucoup d'améliorations. Regardons de façon plus attentive.

Création de la promise

Afin de "promisifier" (désolé pour le néologisme) l'ancien objet XHR, vous devez créer un objet Promise :



Utilisation de la promise

Une fois créée, la promise peut être utilisée pour enchaîner les appels asynchrones de manière plus élégante :



Nous avons donc maintenant (du point de vue de l'utilisateur) :
  • Récupéré la promise (1)
  • Enchaîné avec le code de succès (2 et 3)
  • Enchaîné avec le code d'erreur (4) comme dans un bloc try/catch


Ce qui est intéressant est que les promises chaînées sont facilement appelables à l'aide de .then().then(), etc.

Remarque : Puisque JavaScript est un langage moderne, vous remarquerez peut-être que j'utilise aussi le sucre syntaxique d'ECMAScript 6 comme l'interpolation de chaînes ou la notation fléchée (arrow function).

Terminus : ECMAScript 7 – Asynchronous city

Enfin, nous avons atteint notre destination ! Nous sommes presque dans le futur, mais grâce au cycle de développement rapide d'Edge, l'équipe est en mesure d'introduire un peu d'ECMAScript 7 avec des fonctions async dans la dernière version !

Les functions async sont un sucre syntaxique pour améliorer le modèle au niveau du langage dans le but d'écrire du code asynchrone.

Les fonctions async sont construites par dessus les fonctionnalités d'ECMAScript 6 comme les générateurs. En effet, les générateurs peuvent être utilisées conjointement avec des promises pour produire les mêmes résultats, mais avec beaucoup plus de code d'utilisateur.

On n'a pas besoin de changer la fonction qui génère la promise dans la mesure où les fonctions async travaillent directement avec la promise.

Nous avons seulement besoin de changer la fonction d'appel :

Code javascript : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Let's create an async anonymous function 
(async function() { 
    try { 
        // Just have to await the promise! 
        var json = await getJsonAsync("cards.json"); 
        var result = JSON.parse(json); 
  
        result.collection.forEach(card => { 
            var div = document.createElement("div"); 
            div.innerHTML = `${card.name} cost is ${card.price}`; 
  
            displayDiv.appendChild(div); 
        }); 
    } catch (e) { 
        displayDiv.innerHTML = e; 
    } 
})();

C'est ici que la magie intervient. Ce code ressemble à du code synchrone normal avec une exécution parfaitement linéaire :



Plutôt impressionnant, n'est-ce pas ?

Et la bonne nouvelle est que vous pouvez même utiliser les fonctions async avec la notation fléchée ou les méthodes de classe.

Pour aller plus loin

Si vous voulez plus de détails sur la façon dont nous avons implémenté Chakra, vous pouvez vous rendre sur le message officiel du blog de Edge :

http://blogs.windows.com/msedgedev/2...icrosoft-edge/

Vous pouvez également suivre la progression de l'implémentation d'ECMAScript 6 et 7 dans les différents navigateurs en utilisant le site Web de Kangax : N'hésitez pas également à jeter un oeil sur notre feuille de route JavaScript !

S'il vous plaît, n'hésitez pas à nous donner un retour et à soutenir vos fonctions préférées en utilisant le bouton de vote :



Merci d'avoir lu et nous sommes impatients d'entendre vos commentaires et vos idées !

David Catuhe
Principal Program Manager
@deltakosh


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster un commentaire

Avatar de p3ga5e p3ga5e - Membre confirmé https://www.developpez.com
le 09/10/2015 à 16:50
Afin de compléter cette liste, j’y ajouterai le module suspend/resume qui se base également sur les générateurs ES6 mais étant compatible avec le Modèle D’API par callback d’ES5
Avatar de SylvainPV SylvainPV - Rédacteur/Modérateur https://www.developpez.com
le 09/10/2015 à 17:05
J'adore la syntaxe async/await. Mais je dois avouer que le gros avantage des Promise, c'est que ça t'incite énormément à paralléliser les traitements aynchrones. J'ai peur que la solution de simplicité await conduise à un manque de réflexion et une grosse perte de performances sur les projets avec beaucoup d'asynchronicité (comme pour Node par exemple).
Responsable bénévole de la rubrique JavaScript : Xavier Lecomte -