0% ont trouvé ce document utile (0 vote)
14 vues117 pages

04 NodeJS Callbacks Promesses (6020)

Transféré par

hibapisc
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
14 vues117 pages

04 NodeJS Callbacks Promesses (6020)

Transféré par

hibapisc
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
Vous êtes sur la page 1/ 117

NodeJS

Callbacks,
PROMESSES &
async/await
javascript, ES6 et plus
Préparé par : Larbi HASSOUNI
1
Callbacks

2
Qu’est ce que les fonctions callbacks
• Une fonction callbacks est une fonction qu’on transmet à une autre fonction
comme argument pour une exécution ultérieure.
• Ce qui suit définit une fonction filter() qui accepte un tableau de nombres et
renvoie un nouveau tableau de nombres impairs :

function filter(numbers) {
let results = [];
for (const number of numbers) { Exécution:
if (number % 2 != 0) { [ 1, 7, 3, 5 ]
results.push(number);
}
}
return results;
}
let numbers = [1, 2, 4, 7, 3, 5, 6];
console.log(filter(numbers));
3
• Si vous souhaitez renvoyer un tableau contenant des nombres pairs, vous devez
modifier la fonction filter().
• Pour rendre la fonction filter() plus générique et réutilisable, vous pouvez :
• Tout d'abord, extraire la logique dans le bloc if et encapsuler-la dans une fonction distincte.
• Ensuite, passer la fonction à la fonction filter() en tant qu'argument.
function isOdd(number) {
return number % 2 != 0;
}

function filter(numbers, fn) {


let results = [];
for (const number of numbers) {
Exécution:
if (fn(number)) { [ 1, 7, 3, 5 ]
results.push(number);
}
}
return results;
}
let numbers = [1, 2, 4, 7, 3, 5, 6];
let oddNumbers = filter(numbers, isOdd)
console.log(oddNumbers); 4
• Le résultat est le même. Cependant, vous pouvez passer n'importe
quelle fonction qui accepte un argument et renvoie une valeur
booléenne au deuxième argument de la fonction filter().

• Par exemple, vous pouvez utiliser la fonction filter() pour renvoyer un


tableau de nombres pairs comme le montre le programme suivant:

5
function isOdd(number) {
return number % 2 != 0;
}
function isEven(number) {
return number % 2 == 0;
}

function filter(numbers, fn) {


let results = [];
for (const number of numbers) {
if (fn(number)) {
results.push(number);
}
}
return results;
}

let numbers = [1, 2, 4, 7, 3, 5, 6];

console.log(filter(numbers, isOdd)); //[ 1, 7, 3, 5 ]


console.log(filter(numbers, isEven)); //[ 2, 4, 6 ] 6
• Par définition, isOdd et isEven sont des fonctions callbacks ou des callbacks.
• Comme la fonction filter() accepte une fonction comme argument, on l'appelle
une fonction d'ordre supérieur.
• Un callback peut être une fonction anonyme, qui est une fonction sans nom
comme le montre le programme suivant:

function filter(numbers, callback) {


let results = [];
for (const number of numbers) {
if (callback(number)) {
results.push(number);
}
}
return results;
}
let numbers = [1, 2, 4, 7, 3, 5, 6];
let oddNumbers = filter(numbers, function (number) {return number % 2 != 0;});
console.log(oddNumbers);
7
• Dans cet exemple, nous passons une fonction anonyme à la fonction filter() au
lieu d'utiliser une fonction distincte.
• Dans ES6, vous pouvez utiliser une fonction fléchée comme celle-ci :

function filter(numbers, callback) {


let results = [];
for (const number of numbers) {
if (callback(number)) {
results.push(number);
} There are two types of callbacks: synchronous and
} asynchronous callbacks.
return results;
}

let numbers = [1, 2, 4, 7, 3, 5, 6];

let oddNumbers = filter(numbers, (number) => number % 2 != 0);

console.log(oddNumbers); //[ 1, 7, 3, 5 ]

Il existe deux types de rappels : les rappels synchrones et asynchrones. 8


callbacks Synchrones
• Un callback synchrone est exécuté pendant l'exécution de la fonction
d'ordre supérieur qui utilise le callback.

• isOdd et isEven sont des exemples de callbacks synchrones car ils


s'exécutent lors de l'exécution de la fonction filter().

9
callbacks Asynchrones
• Un callback asynchrone est exécuté après l'exécution de la fonction d'ordre supérieur qui
utilise le callback.

• L'asynchronicité signifie que si JavaScript doit attendre la fin d'une opération, il exécutera
le reste du code en attendant.

• Notez que JavaScript est un langage de programmation à thread unique. Il effectue des
opérations asynchrones via la file d'attente de callbacks et la boucle d'événements.

• Pour les exemples qui suivent pour démontrer l’asynchronicité, nous allons avoir besoin
de la fonction setTimeout() que nous présentons ici.

10
La fonction globale setTimeout()
• La méthode globale setTimeout() définit un timer qui exécute une
fonction ou un morceau de code spécifié une fois le temps spéficié
est expiré.
• La syntaxe est:
❖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)
11
Paramètres de setTimeout()
Paramètre Signification
Une fonction à exécuter après l'expiration du temps
functionRef
du timer.
code Une syntaxe alternative qui vous permet d'inclure
une chaîne au lieu d'une fonction, qui est compilée
et exécutée lorsque le timer expire. Cette syntaxe
n'est pas recommandée pour les mêmes raisons qui
font de l'utilisation de eval() un risque de sécurité.

12
Paramètres de setTimeout() (suite)
Paramètre Signification
delay Le temps, en millisecondes, que le minuteur doit attendre avant que la
fonction ou le code spécifié ne soit exécuté. Si ce paramètre est omis, la
valeur 0 est utilisée, ce qui signifie exécuter "immédiatement", ou plus
précisément, le cycle d'événement suivant.
Notez que dans les deux cas, le délai réel peut être plus long que prévu.

Notez également que si la valeur n'est pas un nombre, un cast de type


implicit est effectué sur la valeur pour la convertir en nombre — ce qui
peut conduire à des résultats inattendus et surprenants.
param1, Arguments supplémentaires qui sont transmis à la fonction
…, paramN spécifiée par functionRef.

13
Valeur retournée par setTimeout()
• La méthode setTimeout() retourne un timeoutID qui est une valeur
entière positive qui identifie le timer créé par l'appel à setTimeout().
Cette valeur peut être passée à clearTimeout() pour annuler le délai
d'attente.

• Il est garanti qu'une valeur timeoutID ne sera jamais réutilisée par un


appel ultérieur à setTimeout() ou setInterval() sur le même objet (une
fenêtre ou un worker).

14
La fonction globale clearTimeout()
La Syntaxe est :
clearTimeout(timeoutID)
• La méthode globale clearTimeout() annule un timeout précédemment établi en
appelant setTimeout().

• Le parameter timeoutID spécifie l'identifiant du délai d'expiration que vous


souhaitez annuler. Cet ID a été renvoyé par l'appel correspondant à setTimeout().

• Si le paramètre fourni n'identifie pas une action précédemment établie, cette


méthode ne fait rien.

15
La fonction globale setInterval()
La syntaxe est :
✓ setInterval(code)
✓ setInterval(code, delay)
✓ setInterval(func)
✓ setInterval(func, delay)
✓ setInterval(func, delay, arg0)
✓ setInterval(func, delay, arg0, arg1)
✓ setInterval(func, delay, arg0, arg1, /* … ,*/ argN)

16
• La méthode setInterval() appelle à plusieurs reprises une fonction ou exécute un
extrait de code, avec un délai fixe entre chaque appel.
• Cette méthode renvoie un ID d'intervalle qui identifie de manière unique
l'intervalle, vous pouvez donc le supprimer ultérieurement en appelant
clearInterval().
• Les paramètres ont la même signification que la méthode setTimeout(), sauf que
le paramètre delay spécifie le temp qui sépare deux exécutions consécutives.
Valeur de retour
L'ID d'intervalle retourné est une valeur numérique non nulle qui identifie le timer
créé par l'appel à setInterval(); cette valeur peut être passée à clearInterval() pour
annuler l'intervalle.

17
La fonction globale clearInterval()
• La syntaxe est:
clearInterval(intervalleID)
• La méthode globale clearInterval() annule une action temporisée et
répétitive précédemment établie par un appel à setInterval().
• intervalleID est l'identifiant de l'action répétée que vous souhaitez annuler.
Cet ID a été renvoyé par l'appel correspondant à setInterval().
• Comme nous avons vu lors de clearTimeout() , Il convient de noter que le
pool d'identifiants utilisés par setInterval() et setTimeout() sont partagés,
ce qui signifie que vous pouvez techniquement utiliser clearInterval() et
clearTimeout() de manière interchangeable.
• Si le paramètre fourni n'identifie pas une action précédemment établie,
cette méthode ne fait rien.
18
Supposons que vous ayez besoin de développer un script qui télécharge
une image à partir d'un serveur distant et de la traiter une fois le
téléchargement terminé :

function download(url) {
// ...
}

function process(picture) {
// ...
}

download(url);
process(picture);

19
• Cependant, le téléchargement d'une image à partir d'un serveur distant prend du
temps en fonction de la vitesse du réseau et de la taille de l'image.
• La fonction download() suivante utilise la fonction setTimeout() pour simuler la
requête réseau :
function download(url) {
setTimeout(() => {
// script to download the picture here
console.log(`Downloading ${url} ... Wait for the
download complete`);
},1000);
}
Et le code suivant émule la fonction process() :
function process(picture) {
console.log(`Processing ${picture}`);
}
20
• Lorsque vous exécutez le code suivant :
let url = 'https://www.yasame.com/pic.jpg';

download(url);
process(url);

vous obtiendrez la sortie suivante :


Processing https://www.yasame.com/pic.jpg
Downloading https://www.yasame.com/pic.jpg ...
Ce n'est pas ce à quoi vous vous attendiez car la fonction process()
s'exécute avant la fonction download().
La séquence correcte devrait être :
▪ Downloading the picture… wait for the download completes.
▪ Process the picture.
21
• Pour résoudre ce problème, vous pouvez passer la fonction process() à la fonction
download() et exécuter la fonction process() dans la fonction download() une fois
le téléchargement terminé, comme le montre le programme ci-dessous:
function download(url, callback) {
setTimeout(() => {
// script to download the picture here
console.log(`Downloading ${url} ...`);

// process the picture once it is completed


callback(url); Exécution:
Downloading https://wwww.yasame.com/pic.jpg ...
}, 1000); Processing https://wwww.yasame.com/pic.jpg
}

function process(picture) {
console.log(`Processing ${picture}`);
}

let url = 'https://wwww.yasame.com/pic.jpg';


download(url, process);
22
• Maintenant, cela fonctionne comme prévu.
• Dans cet exemple, la fonction process() est un callback passé dans
une fonction asynchrone.
• Lorsque vous utilisez un callback pour poursuivre l'exécution du code
après une opération asynchrone, le callback est appelé callback
asynchrone.
• Pour rendre le code plus concis, vous pouvez définir le callback
process() comme une fonction anonyme :

23
function download(url, callback) {
setTimeout(() => {
// script to download the picture here
console.log(`Downloading ${url} ...`);
// process the picture once it is completed
callback(url);

}, 1000);
}

let url = 'https://www.yasame.com/pic.jpg';


download(url, function(picture) {
console.log(`Processing ${picture}`);
});

24
Gestion des erreurs
• La fonction download() suppose que tout fonctionne correctement et ne
considère aucune exception.
• Le code suivant introduit deux callbacks : success et failure pour gérer
respectivement les cas de succès et d'échec :
function download(url, success, failure) {
setTimeout(() => {
console.log(`Downloading the picture from ${url} ...`); Si url est vide (!url), il appelle la
fonction failure. Sinon, il appelle la
!url ? failure(url) : success(url); fonction success.

}, 1000);
On appelle download avec :
} Une URL vide ('').
Une fonction success qui affiche
un message de traitement :
download( Processing the picture ${url}.
Une fonction failure qui affiche un
'', message d'erreur : The '${url}' is
not valid.
(url) => console.log(`Processing the picture ${url}`),
(url) => console.log(`The '${url}' is not valid`)
);

Downloading the picture from ...


The '' is not valid 25
Imbrication des callbacks et la pyramide du Doom
• Comment télécharger trois images et les traiter de manière séquentielle ?
• Une approche typique consiste à appeler la fonction download() à l'intérieur de la
fonction de rappel, comme le montre le programme ci-dessous :

26
function download(url, callback) {
setTimeout(() => { Exécution:
console.log(`Downloading ${url} ...`);
callback(url); Downloading https://www.yasame.com/pic1.jpg ...
Processing https://www.yasame.com/pic1.jpg
}, 1000);
Downloading https://www.yasame.com/pic2.jpg ...
} Processing https://www.yasame.com/pic2.jpg
Downloading https://www.yasame.com/pic3.jpg ...
const url1 = 'https://www.yasame.com/pic1.jpg'; Processing https://www.yasame.com/pic3.jpg
const url2 = 'https://www.yasame.com/pic2.jpg';
Ce code définit une fonction `download` qui simule le téléchargement
const url3 = 'https://www.yasame.com/pic3.jpg'; d'une image depuis une URL après un délai de 1 seconde, puis appelle
une fonction `callback` pour traiter l'URL téléchargée. Il télécharge trois
images de manière séquentielle : d'abord `url1`, puis une fois que `url1`
download(url1, function (url) { est traité, il télécharge `url2`, et enfin `url3`. À chaque étape, il affiche un
message indiquant qu'il télécharge l'image, puis qu'il la traite. Cela crée
console.log(`Processing ${url}`); une structure en cascade où chaque téléchargement et traitement dépend
du précédent.
download(url2, function (url) {
console.log(`Processing ${url}`);
download(url3, function (url) {
console.log(`Processing ${url}`);
});
});
});

27
• Le script fonctionne parfaitement bien.
• Cependant, cette stratégie de callback ne s'adapte pas bien lorsque la
complexité augmente de manière significative.
• L'imbrication de nombreuses fonctions asynchrones dans des callbacks est
connue sous le nom de pyramide du Doom ou enfer des callbacks :

asyncFunction(function(){
asyncFunction(function(){ Pour éviter la pyramide du Doom,
asyncFunction(function(){ nous avons recours aux promesses
asyncFunction(function(){
asyncFunction(function(){ ou aux fonctions async/wait que
//.... nous étudierons par la suite.
});
});
});
});
});

28
NodeJS
Promesses
(promises)

29
PROMISES (Promesses)
• Définition d’un Promise
• Chaînage des promesses
• Méthodes statiques
• Promise.all()
• Promise.race()
• Promise.any()
• Promise.allSettled()
• Promise.prototype.finally()
• Promesse et Gestion des erreurs

30
Pourquoi les promesses
• L'exemple suivant définit une fonction getUsers() qui renvoie une liste
d'objets user :

function getUsers() {
return [
{ username: 'Moha', email: '[email protected]' },
{ username: 'Toto', email: '[email protected]' },
];
}

31
Chaque objet utilisateur a deux propriétés username et email.
Pour rechercher un utilisateur par username dans la liste d'utilisateurs renvoyée
par la fonction getUsers(), vous pouvez utiliser la fonction findUser() comme suit :
function findUser(username) {
const users = getUsers();
const user = users.find((user) => user.username === username);
return user;
}

Dans la fonction findUser() :


Tout d'abord, on obtient un tableau d'utilisateurs en appelant la fonction getUsers ()
Deuxièmement, on recherche l'utilisateur avec un username spécifique à l'aide de la
méthode find() de l'objet Array.
Troisièmement, on renvoie l'utilisateur correspondant.
Ce qui suit montre le code complet pour trouver un utilisateur avec le nom d'utilisateur
‘Moha' :
32
function getUsers() { Ce code JavaScript définit deux fonctions : getUsers et findUser, qui sont utilisées
pour récupérer des utilisateurs à partir d'une liste et rechercher un utilisateur
return [ spécifique par son nom d'utilisateur.

{ username: 'Moha', email: '[email protected]' },


{ username: 'Toto', email: '[email protected]' },
]; La fonction getUsers retourne une liste d'utilisateurs avec leur nom
d'utilisateur et leur adresse e-mail.
}
La fonction findUser prend un nom d'utilisateur en argument et
function findUser(username) { recherche cet utilisateur dans la liste récupérée par la fonction
getUsers. Si l'utilisateur est trouvé, il est retourné. Sinon, undefined
const users = getUsers(); est retourné.

const user = users.find((user) => user.username === username);


return user;
}

console.log(findUser('Moha'));

Exécution:
{ username: 'Moha', email: '[email protected]' }
33
Le code de la fonction findUser() est synchrone et bloquant.
La fonction findUser() exécute la fonction getUsers() pour obtenir un tableau d'utilisateurs, appelle la
méthode find() sur le tableau d'utilisateurs pour rechercher un utilisateur avec un username
spécifique et renvoie l'utilisateur correspondant.

En pratique, la fonction getUsers() peut accéder à une base de données ou appeler une API pour
obtenir la liste des utilisateurs.
Par conséquent, la fonction getUsers() aura un retard.

Pour simuler le délai, vous pouvez utiliser la fonction setTimeout(). Par example:

function getUsers() {
let users = [];
setTimeout(() => {
users = [
{ username: 'Moha', email: '[email protected]' },
{ username: 'Toto', email: '[email protected]' },
];
}, 1000);
return users;
} 34
• Comment cela fonctionne:
• Tout d'abord, on définit un tableau users et initialise sa valeur avec un
tableau vide.
• Ensuite, on affecte un tableau des utilisateurs à la variable users dans
le callback de la fonction setTimeout().
• Troisièmement, on retourne le tableau des utilisateurs
• Le getUsers () ne fonctionnera pas correctement et renvoie toujours
un tableau vide. Par conséquent, la fonction findUser() ne
fonctionnera pas comme prévu :

35
function getUsers() {
let users = [];
setTimeout(() => {
users = [
{ username: 'Moha', email: '[email protected]' },
{ username: 'Toto', email: '[email protected]' },
];
}, 1000); Dans ce code, la fonction `getUsers` utilise `setTimeout` pour simuler une opération
asynchrone qui remplit un tableau d'utilisateurs après une seconde. Cependant, la fonction
return users; `findUser` est immédiatement appelée après `getUsers`, sans attendre que l'opération
} asynchrone soit terminée. Par conséquent, lorsque `findUser` essaie de rechercher un
utilisateur dans le tableau, il est encore vide, ce qui entraîne le retour de `undefined`. Pour
corriger cela, il serait nécessaire de réorganiser le code pour attendre que `getUsers` ait
terminé son travail asynchrone avant d'appeler `findUser`, par exemple en utilisant des
function findUser(username) { promesses ou des callbacks pour gérer le flux d'exécution de manière asynchrone.

const users = getUsers(); // A


const user = users.find((user) => user.username === username); // B
return user;
}

console.log(findUser('Toto'));

Exécution:
undefined 36
• Comme getUsers() renvoie un tableau vide, le tableau users est vide
(ligne A).
• Lors de l'appel de la méthode find() sur le tableau des utilisateurs, la
méthode renvoie undefined (ligne B)
• Le défi est de savoir comment accéder aux utilisateurs renvoyés par la
fonction getUsers() après une seconde.
• Une approche classique consiste à utiliser le callback.

37
Utiliser des callbacks pour gérer une opération asynchrone
• L'exemple suivant ajoute un argument qui est un callback aux fonctions getUsers() et findUser() :
function getUsers(callback1) {
setTimeout(() => {
callback1([
{ username: 'Moha', email: '[email protected]' },
{ username: 'Toto', email: '[email protected]' },
]);
Ce code utilise des fonctions de rappel pour gérer des opérations asynchrones. `getUsers`
}, 1000); simule une tâche asynchrone et invoque ensuite `callback1` avec un tableau d'utilisateurs
} après une seconde. Ensuite, `findUser` utilise `getUsers` pour obtenir les utilisateurs et
recherche un utilisateur spécifié dans le tableau retourné. Cependant, cette approche peut
mener à des résultats imprévisibles car la récupération des utilisateurs n'est pas garantie
d'être terminée avant la recherche de l'utilisateur spécifié. Ainsi, l'utilisation de promesses
function findUser(username, callback2) { serait plus fiable pour gérer l'asynchronisme dans ce scénario.
getUsers((users) => {
const user = users.find((user) => user.username === username);
callback2(user);
});
}

findUser('Toto', console.log);

{ username: 'Toto', email: '[email protected]' } 38


• Dans cet exemple, la fonction getUsers() accepte une fonction callback comme
argument et l'invoque avec le tableau users à l'intérieur de la fonction
setTimeout().
• En outre, la fonction findUser() accepte une fonction callback qui traite
l'utilisateur correspondant.
• L'approche de callback fonctionne très bien. Cependant, cela rend le code plus
difficile à suivre. En outre, cela ajoute de la complexité aux fonctions avec des
arguments callbacks.
• Si le nombre de fonctions augmente, vous pouvez vous retrouver avec le
problème de l'enfer du callback.
• Pour résoudre ce problème, JavaScript propose le concept de promesses
(promises).

39
Qu’est ce que les promesses
• Par définition, une promesse est un objet qui encapsule le résultat
d'une opération asynchrone.
• Un objet promesse a un état qui peut être l'un des suivants :
• Pending (En attentate)
• Fulfilled with a value (succès avec une Valeur)
• Rejected for a reason (Rejeté pour une raison)
• Au début, l'état d’un objet promesse est Pending (en attente),
indiquant que l'opération asynchrone est en cours.
Selon le résultat de l'opération asynchrone, l'état passe à Fullfilled
(rempli) ou Rejected (rejeté).

40
• L'état Fullfilled indique que l'opération asynchrone s'est terminée
avec succès :

• L'état Rejected indique que l'opération asynchrone a échoué.

41
Creation d’une Promesse (un promise)
• Pour créer un objet promesse, vous utilisez le constructeur
Promise() :
const promise = new Promise((resolve, reject) => {
// contient une opération
// ...

// retourne un état
if (success) {
resolve(value);
} else {
reject(error);
}
}); 42
• Resolve et reject sont des noms conventionnel de callback. Il est possible
de les appeler autrement, callback1 et callback2 par exemple:
const promise = new Promise((callback1, callback2) => {
// contient une opération
// ...

// retourne un état
if (success) {
callback1(value);
} else {
callback2(error);
}
});

43
• Le constructeur de promesse accepte une fonction callback qui effectue
généralement une opération asynchrone.
• Cette fonction callback est souvent appelée exécuteur.
• À son tour, l'exécuteur accepte deux fonctions callbacks ayant, par convention,
respectivement les noms resolve et reject.
• Si l'opération asynchrone se termine avec succès, l'exécuteur appellera le callback
resolve() pour changer l'état de la promesse de Pending à Fullfilled avec une
valeur.
• En cas d'erreur durant l’opération asynchrone, l'exécuteur appellera le callback de
reject () pour changer l'état de la promesse de Pending à Rejected avec la raison
error (Rejected with error reason).
• Une fois qu'une promesse atteint l'état Fullfilled ou Rejected, elle reste dans cet
état et ne peut pas passer à un autre état.

44
• En d'autres termes, une promesse ne peut pas passer de l'état
Fullfilled à l'état Rejected et vice versa.
De plus, il ne peut pas revenir de l'état Fullfilled ou Rejected à l'état
Pending.
• Une fois qu'un nouvel objet Promise est créé, son état est Pending. Si
une promesse atteint l'état Fullfilled ou Rejected, elle est résolue.

Notez qu’en pratique vous créerez rarement des objets de promesse. Au lieu
de cela, vous utiliserez les promesses fournies par les bibliothèques.
45
Consommer une promesse
Méthodes then, catch et finally
• 1) La méthode then()
• Pour obtenir la valeur d’un objet promesse lorsqu'elle est Fullfilled, vous appelez
sa méthode then() dont la syntaxe est :

promise.then(onFulfilled,onRejected);

La méthode then() accepte deux fonctions callbacks : onFulfilled et onRejected.


La méthode then() appelle onFulfilled() avec une valeur si la promesse est Fullfilled
(succès de l’operation asynchrone) ou onRejected() avec une erreur si la promesse
est Rejected (Echec de l’opération asynchrone).

Notez que les arguments onFulfilled et onRejected sont facultatifs.

46
Les 3 exemples suivants montrent comment utiliser la méthode then() de l'objet
Promise renvoyé par la fonction getUsers() :

function getUsers() { Crée une nouvelle


Dans cet exemple :
return new Promise((resolve, reject) => { promesse avec deux On définit la function getUsers() qui retourne
fonctions de résolution
setTimeout(() => { Démarre un délai d'exécution d'une
resolve et reject.
un Promise.
fonction après un certain délai.
Puis, on définit la fonction onFulfilled() à
//Recheche de la liste d’users dans une BD avec succès
Après le délai, la promesse est résolue avec succès (resolve) avec un appeler lorsque la promesse est Fullfilled, c-
resolve([ tableau d'utilisateurs. à-d que l’action synchrone s’est terminée
{ username: 'Moha', email: '[email protected]' }, avec succés et appelle le callback resolve.
{ username: 'Toto', email: '[email protected]' }, Puis, on appelle la méthode getUser() pour
]); obtenir l’objet promise.
}, 1000); En dernier, on appelle la méthode then() de
}); // Définition de la fonction onFulfilled qui l'objet promise qui execute le callback
} sera appelée lorsque la promesse est onFullfilled avec l’argument de resolve
résolue avec succès
function onFulfilled(users) { spécifié dans le constructeur du Promise. Ce
Utilise la méthode then sur la promesse
pour spécifier une fonction de rappel qui affiche la liste des utilisateurs sur la
function onFulfilled(users) { (onFulfilled) qui sera appelée lorsque la console.
console.log(users); promesse est résolue avec succès.
En résumé, ce code simule une opération asynchrone
Appelle la fonction getUsers et stocke
} de recherche d'utilisateurs dans une base de données
la promesse retournée dans la
variable promise.
Exécution: en utilisant des promesses. Une fois que la promesse
est résolue avec succès (c'est-à-dire que les utilisateurs
const promise = getUsers(); [
sont récupérés avec succès), la fonction onFulfilled est
appelée pour afficher les utilisateurs dans la console.
promise.then(onFulfilled); { username: 'Moha', email: '[email protected]' },
{ username: 'Toto', email: '[email protected]' }
] 47
Pour rendre le code plus concis, vous pouvez utiliser une fonction fléchée comme
argument de la méthode then() comme ceci :
function getUsers() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve([
{ username: 'Moha', email: '[email protected]' },
{ username: 'Toto', email: '[email protected]' },
]);
}, 1000);
});
}

const promise = getUsers();


[
promise.then((users) => { { username: 'Moha', email: '[email protected]' },
console.log(users); { username: 'Toto', email: '[email protected]' }
}); ]

Dans cet exemple, la fonction getUsers() réussit toujours. Pour simuler l'erreur, nous pouvons utiliser
un indicateur de réussite comme le montre le programme suivant : 48
function getUsers() {
return new Promise((resolve, reject) => {
setTimeout(() => {
//Recherche d'une liste de users mais sans succès
reject("Erreur : Impossible d'obtenir la liste des users");
}, 1000);
});
}

function onRejected(error) {
console.log(error);
}

const promise = getUsers();


promise.then(undefined, onRejected);

Exécution:
Erreur : Impossible d'obtenir la liste des users 49
Comment cela fonctionne:
let success = true;
On définit d’abord la variable success
function getUsers() {
et initialise sa valeur à true.
return new Promise((resolve, reject) => {
setTimeout(() => {
Si le succès est vrai, la promesse dans
//Recheche de la liste d’utilisateur dans une BD
la fonction getUsers() est Fullfilled et
if (success) {
on appelle le callback resolve avec
resolve([
une liste d'utilisateurs comme
{ username: 'Moha', email: '[email protected]' },
argument. Sinon, on appelle le
{ username: 'Toto', email: '[email protected]' },
callback rejected avec un message
]);
d'erreur.
} else {
2ème, on définit les callbacks
reject("Impossible d'obtenir la liste des users");
onFulfilled et onRejected.
}
3ème, On crée l’objet promise par
}, 1000); [
invocation de la fonction getUsers() .
}); { username: 'Moha', email: '[email protected]' },
{ username: 'Toto', email: '[email protected]' } En dernier, on exécute la méthode de
} ]
then() en lui passant les deux callbacks
onFulfilled (qui correspond à resolve)
function onFulfilled(users) { console.log(users);}
et onRejected qui correspond à reject.
function onRejected(error) { console.log(error);}
Dans ce cas c’est onFullfilled qui sera
exécutée
const promise = getUsers();
promise.then(onFulfilled, onRejected); 50
L’exemple suivant montre comment utiliser les fonctions fléchées comme arguments
de la méthode then() :
let success = true;

function getUsers() {
return new Promise((resolve, reject) => {
setTimeout(() => {
//Recherche de la liste de users dans la BD
if (success) {
resolve([
{ username: 'Moha', email: '[email protected]' },
{ username: 'Toto', email: '[email protected]' },
]);
} else {
reject('Failed to the user list');
}
}, 1000);
});
}

const promise = getUsers();


promise.then((users) => console.log(users), (error) => console.log(error)); 51
2) La méthode catch()
• Si vous souhaitez obtenir l'erreur uniquement lorsque l'état
de la promesse est rejeté, vous pouvez utiliser la méthode
catch() de l'objet Promise.
• La syntaxe est :
promise.catch(onRejected);
• En interne, la méthode catch() invoque la méthode
then(undefined, onRejected).

• L'exemple suivant modifie l'indicateur de réussite success sur


false pour simuler le scénario d'erreur :

52
let success = false;

function getUsers() {
return new Promise((resolve, reject) => {
setTimeout(() => {
//Recherche d'une lise de users sans succès
if (success) {
resolve([
{ username: ‘Moha', email: ‘[email protected]' },
{ username: ‘Toto', email: ‘[email protected]' },
]);
} else {
reject("Echec d'obtention de la liste de users");
}
}, 1000);
});
}

const promise = getUsers();

promise.catch((error) => {
console.log(error);
53
});
• 3) La méthode finally()
• Parfois, vous souhaitez exécuter le même morceau de code, que la promesse soit
remplie ou rejetée. Par example:
const render = () => {
const render = () => { //...
//... };
};
getUsers()
getUsers() .then((users) => {
.then((users) => { console.log(users);
console.log(users); })
render(); .catch((error) => {
}) console.log(error);
.catch((error) => { }) Utilise la méthode finally pour spécifier une
console.log(error); .finally(() => { fonction de rappel qui sera exécutée dans
tous les cas, que la promesse soit résolue
render(); render(); ou rejetée. Dans ce cas, il appelle la
fonction render.
}); });

Comme vous pouvez le voir, l'appel de la fonction render() est dupliqué dans les méthodes then() et catch().
Pour supprimer ce doublon et exécuter le render() que la promesse soit remplie ou rejetée, vous utilisez la
méthode finally(), comme ceci : 54
Un exemple pratique de promise

• L'exemple suivant montre comment charger un fichier JSON à partir du


serveur et afficher son contenu sur une page Web.
• Supposons que vous ayez le fichier JSON suivant :
https://www.yasame.com/api.json
• avec le contenu suivant :

{
"message": "Démonstration de la promesse JavaScript"
}
• L'exemple suivant montre la page HTML qui contient un bouton. Lorsque
vous cliquez sur le bouton, la page charge les données du fichier JSON et
affiche le message :

55
Fichier html
<!DOCTYPE html> Fichier style.css
<html> #container{
<head> width: 50%;
<meta charset="utf-8"> height: 50%;
<title>JavaScript Promise Demo</title> border: 2px solid blue;
<link href="css/style.css" }
rel="stylesheet"> #message{
</head> width: 25%;
<body> height: 25%;
<div id="container"> border: 1px solid red;
<div id="message"></div> }
<button id="btnGet">Get
Message</button>
</div> Fichier api.json
<script src="js/promise-demo.js">
</script> {
</body> "message": "JavaScript Promise Demo"
</html> }

56
function load(url) { Crée et retourne une nouvelle promesse. La promesse prend
une fonction avec deux arguments resolve et reject, utilisés pour
return new Promise(function (resolve, reject) { gérer la réussite ou l'échec de la requête AJAX.

Fichier : promise-demo.js
const request = new XMLHttpRequest(); Crée une nouvelle instance de XMLHttpRequest, qui est
request.onreadystatechange = function () { utilisée pour effectuer la requête AJAX.

if (this.readyState === 4 && this.status == 200) { resolve(this.response);}


else {reject(this.status); } Définit une fonction de rappel qui sera exécutée chaque fois que l'état
(readyState) de la requête change.
};
Il définit une fonction load qui prend une URL en paramètre et
request.open('GET', url, true);Ouvre une nouvelle
requête GET à l'URL retourne une promesse pour effectuer une requête AJAX.
request.send(); spécifiée. Le troisième Lorsque le bouton #btnGet est cliqué, il déclenche une requête AJAX
argument (true) indique en utilisant la fonction load.
}); que la requête est Si la requête est réussie, il met à jour le contenu de l'élément
asynchrone. #message avec le message récupéré du fichier JSON.
} En cas d'erreur, il affiche un message d'erreur avec le statut HTTP de
const url = 'https://www.yasame.com/api.json' la requête.
const btn = document.querySelector('#btnGet');
const msg = document.querySelector('#message');
btn.addEventListener('click', () => {
load(URL)
.then((response) => {
const result = JSON.parse(response);
msg.innerHTML = result.message;
})
.catch((error) => {
msg.innerHTML = `Error getting the message, HTTP status: ${error}`;
});
57
});
• Comment cela fonctionne:
• Tout d'abord, définissez la fonction load() qui utilise l'objet XMLHttpRequest pour
charger le fichier JSON depuis le serveur.
• Dans l'exécuteur, nous appelons la fonction resolve () avec la réponse
(resolve(this.response);)si le code d'état HTTP est 200. Sinon, nous
invoquons la fonction rejet () avec le code d'état HTTP
(reject(this.status);).

• Deuxièmement, enregistrez l'écouteur d'événement de clic sur le bouton et


appelez la méthode then() de l'objet promise. Si le chargement réussit, nous
affichons le message renvoyé par le serveur. Sinon, nous affichons le message
d'erreur avec le code d'état HTTP.

58
Chaînage des promesses

59
Introduction au chaînage des promesse
(Promises chaining)
• Parfois, vous souhaitez exécuter deux ou plusieurs opérations
asynchrones liées, l'opération suivante commençant par le résultat de
l'étape précédente. Par exemple:
• On commence par créer une nouvelle promesse qui se résout au
nombre 10 après 3 secondes :
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10);
}, 3 * 1000);
});
Notez que la fonction setTimeout() simule une opération asynchrone.
60
• Invoquez ensuite la méthode then() de la promesse :
p.then((result) => {
console.log(result);
return result * 2;
});

Le callback passé à la méthode then() s'exécute une fois la promesse résolue.


Dans le callback, nous montrons le résultat de la promesse et renvoyons une nouvelle valeur
multipliée par deux (result*2).

Étant donné que la méthode then() renvoie une nouvelle promesse avec une valeur résolue en une
valeur, vous pouvez appeler la méthode then() sur la promesse de retour comme ceci :
p.then((result) => {
console.log(result);
Dans cet exemple, la valeur de retour de la première
return result * 2;
méthode then() est transmise à la seconde méthode then()
}).then((result) => {
console.log(result);
return result * 3;
});
61
• Vous pouvez continuer à appeler la méthode then() successivement comme suit :

let p = new Promise((resolve, reject) => {


setTimeout(() => {
resolve(10);
Après un délai de 3 secondes (3000
}, 3 * 1000); millisecondes), la promesse est
résolue avec la valeur 10.
});
Exécution:
p.then((result) => {
console.log(result); // 10 10
return result * 2; 20
}).then((result) => { 60
console.log(result); // 20
return result * 3;
}).then((result) => {
console.log(result); // 60
return result * 4;
});

62
• La façon dont nous appelons les méthodes then() comme celle-ci est souvent
appelée chaîne de promesses.
• L'image suivante illustre la chaîne de promesse :

63
Plusieurs gestionnaires pour une promesse
• Lorsque vous appelez la méthode then() plusieurs fois sur une promesse, ce n'est
pas le chaînage de la promesse. Par exemple:

let p = new Promise((resolve, reject) => {


setTimeout(() => {
resolve(10); Ce code crée une promesse qui se résout avec
}, 3 * 100); la valeur `10` après un délai de 300
millisecondes, puis attache deux appels `then`
}); indépendants à cette promesse. Chaque `then`
reçoit la valeur résolue de la promesse et
l'affiche, mais comme ils ne sont pas en chaîne,
p.then((result) => { chacun reçoit initialement la valeur `10` et
retourne respectivement `20` et `30`, bien que
console.log(result); // 10 ces valeurs ne soient pas utilisées
return result * 2; ultérieurement dans le code.

})

p.then((result) => { Exécution:


console.log(result); // 10
return result * 3; 10
}) 10
64
• Dans l’exemple précédent, nous avons plusieurs gestionnaires pour une promesse.
• Ces gestionnaires n'ont aucune relation.
• De plus, ils s'exécutent indépendamment et ne transmettent pas le résultat de l'un à
l'autre comme la chaîne de promesses.

• L'image suivante illustre une promesse qui a plusieurs gestionnaires :

• En pratique, vous utiliserez rarement plusieurs gestionnaires pour une promesse.


65
Retourner une promesse
• Lorsque vous renvoyez une valeur dans la méthode then(), la méthode
then() renvoie une nouvelle Promise qui se résout immédiatement en la
valeur de retour.
• De plus, vous pouvez renvoyer une nouvelle promesse dans la méthode
then(), comme le montre le programme suivant:

66
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(10); Cet exemple montre 10, 20 et 60
}, 3 * 100); toutes les 3 secondes.
}); Ce modèle de code vous permet
p.then((result) => { d'exécuter certaines tâches en
console.log(result); séquence.
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(result * 2);
}, 3 * 1000);
});
}).then((result) => {
console.log(result);
return new Promise((resolve, reject) => {
setTimeout(() => { Exécution:
resolve(result * 3);
}, 3 * 1000); 10
}); 20
}).then(result => console.log(result)); 60
67
• Ce qui suit a modifié l'exemple ci-dessus :
function generateNumber(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num);
}, 3 * 1000);
});
} Appelle generateNumber avec 10, créant une promesse qui sera
résolue avec 10 après 3 secondes.

generateNumber(10) Le premier then est exécuté lorsque la promesse est


résolue. Il affiche 10, puis appelle generateNumber
.then((result) => { avec 20 (10 * 2), retournant une nouvelle promesse.
console.log(result);
return generateNumber(result * 2);
})
.then((result) => { Exécution:
console.log(result);
return generateNumber(result * 3); 10
}) 20
.then((result) => console.log(result)); 60
68
Syntaxe de chaînage des promesses

• Parfois, vous avez plusieurs tâches asynchrones que vous souhaitez


exécuter en séquence.
• De plus, vous devez transmettre le résultat de l'étape précédente à la
suivante.
• Dans ce cas, vous pouvez utiliser la syntaxe suivante :

step1()
.then(result => step2(result))
.then(result => step3(result))
...

69
• Si vous avez besoin de passer le résultat de la tâche précédente à la
suivante sans passer le résultat, vous utilisez cette syntaxe :
step1()
.then(step2)
.then(step3)
...

Supposons que vous souhaitiez effectuer les opérations asynchrones suivantes


dans l'ordre :

• Tout d'abord, récupérez l'utilisateur à partir de la base de données.


• Deuxièmement, obtenez les services de l'utilisateur sélectionné.
• Troisièmement, calculez le coût du service à partir des services de l'utilisateur.
Les fonctions suivantes illustrent les trois opérations asynchrones :
70
function getUser(userId) {
return new Promise((resolve, reject) => {
console.log('Get the user from the database.');
setTimeout(() => {resolve({userId: userId, username: 'admin’ });}, 1000);
});
}

function getServices(user) {
return new Promise((resolve, reject) => {
console.log(`Get the services of ${user.username} from the API.`);
setTimeout(() => {resolve(['Email', 'VPN', 'CDN’]);}, 3 * 1000);
});
}

function getServiceCost(services) {
return new Promise((resolve, reject) => {
console.log(`Calculate the service cost of ${services}.`);
setTimeout(() => { resolve(services.length * 100);}, 2 * 1000);
});
}

71
• Ce qui suit utilise les promesses pour sérialiser les séquences :

getUser(100)
.then(getServices)
.then(getServiceCost)
.then(console.log);

Exécution:

Get the user from the database.


Get the services of admin from the API.
Calculate the service cost of Email,VPN,CDN.
300

Notez que ES2017 a introduit l'async/await qui vous aide à écrire le code qui est
plus propre que d'utiliser la technique de chaînage de promesses.
72
Méthodes statique de Promise:
▪ all()
▪ race()

73
Méthode Promise.all()

74
Introduction à la méthode Promise.all()
• La méthode statique Promise.all() prend un itérable de promesses :
Promise.all(iterable);

La méthode Promise.all() renvoie une seule promesse qui se résout


lorsque toutes les promesses d'entrée ont été résolues.
La promesse renvoyée se résout en un tableau des résultats des
promesses d'entrée :

75
• Dans ce diagramme, la promesse1 se résout en une valeur v1 à t1 et la
promesse2 se résout en une valeur v2 à t2.
• Par conséquent, Promise.all(promise1, promise2) renvoie une promesse qui se
résout en un tableau contenant les résultats de promise1 et promise2 [v1, v2] à
t2.
• En d'autres termes, Promise.all() attend que toutes les promesses d'entrée soient
résolues et renvoie une nouvelle promesse qui se résout en un tableau contenant
les résultats des promesses d'entrée.
76
• Si l'une des promesses d'entrée est rejetée, la méthode Promise.all() renvoie
immédiatement une promesse qui est rejetée avec une erreur de la première
promesse rejetée :

Dans ce diagramme, le promise2 rejette à t1 avec une erreur. Par conséquent,


Promise.all() renvoie une nouvelle promesse qui la rejette immédiatement avec la
même erreur. De plus, Promise.all() ne se soucie pas des autres promesses d'entrée,
qu'elles soient résolues ou rejetées.
En pratique, Promise.all() est utile pour agréger les résultats de plusieurs opérations
asynchrones. 77
Exemples d’utilisation de la méthode Promise.all()

1) Exemple de promesses résolues


Les promesses suivantes se résolvent à 10, 20 et 30 après 1, 2 et 3
secondes. Nous utilisons setTimeout() pour simuler les opérations
asynchrones :

78
const p1 = new Promise((resolve, reject) => { Lorsque toutes les promesses ont été
setTimeout(() => { résolues, les valeurs de ces promesses sont
console.log('The first promise has resolved'); transmises au callback de la méthode
resolve(10); }, 1 * 1000); then() sous forme de tableau.
});
const p2 = new Promise((resolve, reject) => { Dans le callback, nous utilisons la méthode
setTimeout(() => { reduce() de Array pour calculer la valeur
console.log('The second promise has resolved'); totale et utilisons console.log pour afficher
resolve(20);}, 2 * 1000); le tableau de valeurs ainsi que le total.
}); Ce code utilise `Promise.all` pour exécuter trois
const p3 = new Promise((resolve, reject) => { promesses (`p1`, `p2`, `p3`) en parallèle et
attend qu'elles soient toutes résolues. Une fois
setTimeout(() => { résolues, il passe leurs résultats sous forme de
tableau à la fonction `then`. Dans cette
console.log('The third promise has resolved'); fonction, il utilise `reduce` pour calculer la
resolve(30);}, 3 * 1000); somme des résultats et les affiche dans la
console, suivie du total calculé.
}); Exécution:

Promise.all([p1, p2, p3]).then((results) => { The first promise has resolved


const total = results.reduce((p, c) => p + c); The second promise has resolved
console.log(`Results: ${results}`); The third promise has resolved
console.log(`Total: ${total}`); Results: 10,20,30
}); Total: 60
79
2) Exemple de promesses rejetées
• La Promise.all() renvoie une Promise qui est rejetée si l'une des
promesses d'entrée est rejetée.

80
const p1 = new Promise((resolve, reject) => { Dans cet exemple, nous avons trois
setTimeout(() => { promesses : la première est résolue
console.log('The first promise has resolved'); après 1 seconde, la seconde est
resolve(10);}, 1 * 1000); rejetée après 2 secondes et la
troisième est résolue après 3
}); secondes.
const p2 = new Promise((resolve, reject) => {
setTimeout(() => { Par conséquent, la promesse
console.log('The second promise has rejected'); retournée est rejetée car la deuxième
reject('Failed');}, 2 * 1000); promesse est rejetée. La méthode
}); catch() est exécutée pour afficher la
const p3 = new Promise((resolve, reject) => { raison de la promesse rejetée.
setTimeout(() => {
console.log('The third promise has resolved');
resolve(30);}, 3 * 1000); Exécution:
});
The first promise has resolved
Promise.all([p1, p2, p3]) The second promise has rejected
.then(console.log) // never execute Failed
.catch(console.log); The third promise has resolved

81
Méthode Promise.race()

82
Introduction à la méthode statique Promise.race()
• La méthode statique Promise.race() accepte une liste de promesses
en tant qu'objet itérable et renvoie une nouvelle promesse qui
remplit ou rejette dès qu'il y a une promesse qui remplit ou rejette,
avec la valeur ou la raison de cette promesse.
• La syntaxe de la méthode Promise.race() est :
Promise.race(iterable)

• Dans cette syntaxe, l'itérable est un objet itérable qui contient une
liste de promesses.
• Le nom de Promise.race() implique que toutes les promesses
s'affrontent avec un seul gagnant, résolu ou rejeté.
• Voir le schéma suivant :
83
Diagramme du fonctionnement de la méthode race

Dans ce schéma :
• La promesse1 est remplie avec la valeur v1 à t1.
• La promesse2 est rejetée avec l'erreur à t2.
• Parce que la promesse1 est résolue plus tôt que la promesse2, la
promesse1 remporte la course.
• Par conséquent, Promise.race([promise1, promise2]) renvoie une nouvelle
promesse qui est remplie avec la valeur v1 à t1. 84
Exemples d’utilisation de Promise.race()

1) Exemples simples de Promise.race()


• Le programme qui suit crée deux promesses :
• l'une se résout en 1 seconde et l'autre se résout en 2 secondes.
• Étant donné que la première promesse se résout plus rapidement
que la seconde, Promise.race() se résout avec la valeur de la
première promesse :

85
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('The first promise has resolved');
resolve(10);
}, 1 * 1000);

});

const p2 = new Promise((resolve, reject) => {


setTimeout(() => {
console.log('The second promise has resolved');
resolve(20);
}, 2 * 1000);
});
Exécution:
Promise.race([p1, p2]) The first promise has resolved
.then(value => console.log(`Resolved: ${value}`)) Resolved: 10
.catch(reason => console.log(`Rejected: ${reason}`)); The second promise has resolved

86
• L'exemple suivant crée deux promesses. La première promesse se résout en 1
seconde tandis que la seconde la rejette en 2 secondes. Étant donné que la
première promesse est plus rapide que la seconde, la promesse renvoyée se
résout à la valeur de la première promesse :

const p1 = new Promise((resolve, reject) => {


setTimeout(() => {
console.log('The first promise has resolved');
resolve(10); }, 1 * 1000);

});

const p2 = new Promise((resolve, reject) => {


setTimeout(() => {
console.log('The second promise has rejected');
reject(20);}, 2 * 1000);
}); Exécution:

Promise.race([p1, p2]) The first promise has resolved


.then(value => console.log(`Resolved: ${value}`)) Resolved: 10
.catch(reason => console.log(`Rejected: ${reason}`)); The second promise has rejected
87
Notez que si la deuxième promesse était plus rapide que la première, la promesse
de retour serait rejetée avec la raison de la deuxième promesse. Le programme ci-
dessous le montre.

const p1 = new Promise((resolve, reject) => {


setTimeout(() => {
console.log('The first promise has resolved');
resolve(10); }, 2 * 1000);

});

const p2 = new Promise((resolve, reject) => {


setTimeout(() => {
console.log('The second promise has rejected');
reject(20);}, 1 * 1000);
}); Exécution:

Promise.race([p1, p2]) The second promise has rejected


.then(value => console.log(`Resolved: ${value}`)) Rejected: 20
.catch(reason => console.log(`Rejected: ${reason}`)); The first promise has resolved
88
Gestion des erreurs de promesse

89
• Supposons que vous ayez une fonction appelée getUserById() qui
renvoie une Promise :
function getUserById(id) {
return new Promise((resolve, reject) => {
resolve({
id: id,
username: 'admin'
});
});
}

90
Erreur normale:
• Commencer par modifier la fonction getUserById() pour générer une
erreur en dehors de la promesse :
function getUserById(id) {
if (typeof id !== 'number' || id <= 0) {
throw new Error('Invalid id argument');
}

return new Promise((resolve, reject) => {


resolve({
id: id,
username: 'admin'
});
});
}

91
• Deuxièmement, gérez la promesse en utilisant à la fois les méthodes
then() et catch() :
getUserById('a')
.then(user => console.log(user.username))
.catch(err => console.log(err));

Le code renvoie une erreur :


throw new Error('Invalid id argument');
^
Error: Invalid id argument

92
Lorsque vous déclenchez une exception en dehors de la promesse,
vous devez l'attraper avec try/catch :
try {
getUserById('a')
.then(user => console.log(user.username))
.catch(err => console.log(`Caught by .catch ${error}`));
} catch (error) {
console.log(`Caught by try/catch ${error}`);
}

Exécution:

Caught by try/catch Error: Invalid id argument

93
Erreurs à l’intérieur des promesses
• Nous modifions la fonction getUserById() pour générer une erreur dans la
promesse :

let authorized = false;

function getUserById(id) {
return new Promise((resolve, reject) => {
if (!authorized) {
throw new Error('Unauthorized access to the user data');
}

resolve({
id: id,
username: 'admin'
});
});
}
94
• Et consommer la promesse :
try {
getUserById(10)
.then(user => console.log(user.username))
.catch(error => console.log(`Caught by .catch ${error}`));
} catch (error) {
console.log(`Caught by try/catch ${error}`);
}

Exécution:
Caught by .catch Error: Unauthorized access to the user data

Si vous lancez une erreur dans la promesse, la méthode catch() l'attrapera, pas le try/catch.

95
• Si vous enchaînez des promesses, la méthode catch() détectera les erreurs
survenues dans n'importe quelle promesse. Par exemple:

promise1
.then(promise2)
.then(promise3)
.then(promise4)
.catch(err => console.log(err));

Dans cet exemple, s'il y a une erreur dans la promesse1, la promesse2


ou la promesse4, la méthode catch() la traitera.

96
Appel de la fonction reject()

• Lancer une erreur a le même effet que d'appeler le reject() comme


illustré dans l'exemple suivant :

97
let authorized = false;

function getUserById(id) {
return new Promise((resolve, reject) => {
if (!authorized) { Dans cet exemple, au lieu de
reject('Unauthorized access to the user data'); générer une erreur à
} l'intérieur de la promesse,
nous avons appelé
resolve({ explicitement le rejet ().
id: id, La méthode catch() gère
username: 'admin' également l'erreur dans ce
}); cas.
});
}

try {
getUserById(10)
.then(user => console.log(user.username))
.catch(err => console.log(`Caught by .catch ${err}`));
} catch (error) {
console.log(`Caught by try/catch ${error}`);
}

Caught by .catch Unauthorized access to the user data 98


Absence de la méthode catch()
• L'exemple suivant ne fournit pas la méthode catch() pour gérer
l'erreur dans la promesse.
• Cela provoquera une erreur d'exécution et terminera le programme :

99
let authorized = false;

function getUserById(id) { Si la promesse est résolue, vous


return new Promise((resolve, reject) => { pouvez omettre la méthode
if (!authorized) { catch().
reject('Unauthorized access to the user data'); À l'avenir, une erreur potentielle
} peut entraîner l'arrêt inattendu du
resolve({ programme.
id: id,
username: 'admin'
});
});
}

try {
getUserById(10)
.then(user => console.log(user.username));
// the following code will not execute
console.log('next');

} catch (error) {
console.log(`Caught by try/catch ${error}`);
} 100
NodeJS
ASYNC / AWAIT

101
Introduction
• Auparavant, pour gérer les opérations asynchrones, on utilisait
souvent les fonctions de Callbacks.
• Cependant, lorsqu’on imbrique de nombreuses fonctions Callback, le
code sera plus difficile à maintenir. Et on se retrouve avec un
problème notoire connu sous le nom d'enfer des callbacks.
• Supposons que vous deviez effectuer trois opérations asynchrones
dans l'ordre suivant :
• Sélectionner l'utilisateur dans la base de données.
• Obtenir les services de l'utilisateur à partir d'une API.
• Calculer le coût du service en fonction des services à partir d’un serveur.
• Les fonctions ci-après illustrent les trois tâches.
• Notez que nous utilisons la fonction setTimeout() pour simuler
l'opération asynchrone.
102
Utilisation des Callbacks
function getUser(userId, callback) {
console.log('Get user from the database.');
setTimeout(() => {
console.log("getUser : Interieur de setTimeout ");
callback({
userId: userId,
username: 'Moha'
});
console.log("getUser : Après Callback ");
}, 1000);
}

function getServices(user, callback) {


console.log(`Get services of ${user.username} from the
API.`);
setTimeout(() => {
console.log("getServices : Interieur de setTimeout ");
callback(['Email', 'VPN', 'CDN']);
console.log("getServices : Après Callback ");
}, 2 * 1000);
} 103
function getServiceCost(services, callback) {
console.log(`Calculate service costs of ${services}.`);
setTimeout(() => {
console.log("getServicesCost : Avant CallbacksetTimeout ");
callback(services.length * 100);
console.log("getServicesCost : Après Callback ");
}, 3 * 1000);
}

getUser(100, (user) => {


getServices(user, (services) => {
getServiceCost(services, (cost) => {
console.log(`The service cost is ${cost}`);
});
});
});

104
105
Exécution:
Get user from the database.
getUser : Interieur de setTimeout
Get services of Moha from the API.
getUser : Après Callback
getServices : Interieur de setTimeout
Calculate service costs of Email,VPN,CDN.
getServices : Après Callback
getServicesCost : Avant CallbacksetTimeout
The service cost is 300
getServicesCost : Après Callback

106
Utilisation des Promesses

• Pour éviter ce problème d'enfer de Callback, ES6 a introduit les


promesses qui vous permettent d'écrire du code asynchrone de
manière plus gérable.

• Tout d'abord, vous devez renvoyer une Promise dans chaque


fonction :

107
function getUser(userId) {
return new Promise((resolve, reject) => {
console.log('Get user from the database.');
setTimeout(() => {
resolve({
userId: userId,
username: 'Moha'
});
}, 1000);
})
}

function getServices(user) {
return new Promise((resolve, reject) => {
console.log(`Get services of ${user.username} from the API.`);
setTimeout(() => {
resolve(['Email', 'VPN', 'CDN']);
}, 2 * 1000);
});
}

108
function getServiceCost(services) {
return new Promise((resolve, reject) => {
console.log(`Calculate service costs of ${services}.`);
setTimeout(() => {
resolve(services.length * 100);
}, 3 * 1000);
});
}

getUser(100)
.then(getServices)
.then(getServiceCost)
.then(console.log);

Exécution:

Get user from the database.


Get services of Moha from the API.
Calculate service costs of Email,VPN,CDN.
300
109
• ES2017 a introduit les mots-clés async/wait qui s'appuient sur les
promesses, vous permettant d'écrire du code asynchrone qui
ressemble plus à du code synchrone et plus lisible.
• Techniquement parlant, l'async / await est du sucre syntaxique pour
les promesses.
• Si une fonction renvoie une Promise, vous pouvez placer le mot clé
await devant l'appel de la fonction, comme ceci :
let result = await f();
• await attendra que la promesse renvoyée par le f() soit réglée.
• Le mot clé await ne peut être utilisé qu'à l'intérieur des fonctions
asynchrones.
• Ce qui suit définit une fonction asynchrone qui appelle les trois
opérations asynchrones dans l'ordre :

110
function getUser(userId) {
return new Promise((resolve, reject) => {
console.log('Get user from the database.');
setTimeout(() => {
resolve({
userId: userId,
username: 'Moha'
});
}, 1000);
})
}

function getServices(user) {
return new Promise((resolve, reject) => {
console.log(`Get services of ${user.username} from the API.`);
setTimeout(() => {
resolve(['Email', 'VPN', 'CDN']);
}, 2 * 1000);
});
}

111
function getServiceCost(services) {
return new Promise((resolve, reject) => {
console.log(`Calculate service costs of ${services}.`);
setTimeout(() => {
resolve(services.length * 100);
}, 3 * 1000);
});
}

async function showServiceCost() {


let user = await getUser(100);
let services = await getServices(user);
let cost = await getServiceCost(services);
console.log(`The service cost is ${cost}`);
}

showServiceCost();

Exécution: Notez que le code asynchrone


Get user from the database. ressemble maintenant au code
Get services of Moha from the API. synchrone.
Calculate service costs of Email,VPN,CDN.
The service cost is 300 112
Le mot-clé async
• Le mot-clé async vous permet de définir une fonction qui gère les opérations
asynchrones.
• Pour définir une fonction asynchrone, vous placez le mot-clé async devant le mot-
clé function comme suit :
async function sayHi() {
return 'Hi';
}
Les fonctions asynchrones s'exécutent de manière asynchrone via la boucle
d'événements. Il renvoie toujours une promesse.
Dans cet exemple, comme la fonction sayHi() renvoie une Promise, vous pouvez
la consommer, comme ceci :
sayHi().then(console.log);
113
Vous pouvez également renvoyer explicitement une promesse à partir de la
fonction sayHi(), comme indiqué dans le code suivant :
async function sayHi() {
return Promise.resolve('Hi');
L'effet est le même.
}

Outre les fonctions régulières, vous pouvez utiliser le mot clé async dans les
expressions de fonction :
let sayHi = async function () {
return 'Hi';
}

fonctions fléchées : let sayHi = async () => 'Hi';


class Greeter {
async sayHi() {
et méthodes de classes : return 'Hi';
}
}
114
Le mot clé await
• Vous utilisez le mot clé await pour attendre qu'une promesse soit
réglée dans un état résolu ou rejeté.
• Vous ne pouvez utiliser le mot clé await qu'à l'intérieur d'une fonction
asynchrone :
async function display() {
let result = await sayHi();
console.log(result);
}

Dans cet exemple, le mot clé await indique au moteur JavaScript d'attendre que la
fonction sayHi() se termine avant d'afficher le message.

Notez que si vous utilisez l'opérateur await en dehors d'une fonction asynchrone,
115
vous obtiendrez une erreur.
Gestion des erreurs
• Si une promesse est résolue, la promesse await renvoie le résultat.
• Cependant, lorsque la promesse est rejetée, la promesse await lèvera une erreur
comme s'il y avait une instruction throw.
• Les deux codes ci-dessous sont identiques
async function getUser(userId) {
await Promise.reject(new Error('Invalid User Id'));
}

async function getUser(userId) {


throw new Error('Invalid User Id');
}

Dans le scénario réel, il faudra un certain temps pour que la promesse génère
une erreur.

116
• Vous pouvez intercepter l'erreur en utilisant l'instruction try...catch, de la même
manière qu'une instruction throw normale :
async function getUser(userId) {
try {
const user = await Promise.reject(new Error('Invalid User Id'));
} catch(error) {
console.log(error);
}
}

Il est possible d'intercepter les erreurs causées par une ou plusieurs promesses await :
async function showServiceCost() {
try {
let user = await getUser(100);
let services = await getServices(user);
let cost = await getServiceCost(services);
console.log(`The service cost is ${cost}`);
} catch(error) {
console.log(error);
}
} 117

Vous aimerez peut-être aussi