04 NodeJS Callbacks Promesses (6020)
04 NodeJS Callbacks Promesses (6020)
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;
}
5
function isOdd(number) {
return number % 2 != 0;
}
function isEven(number) {
return number % 2 == 0;
}
console.log(oddNumbers); //[ 1, 7, 3, 5 ]
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.
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.
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().
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);
function process(picture) {
console.log(`Processing ${picture}`);
}
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);
}
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`)
);
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;
}
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.
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);
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 :
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);
46
Les 3 exemples suivants montrent comment utiliser la méthode then() de l'objet
Promise renvoyé par la fonction getUsers() :
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);
}
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);
});
}
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);
});
}
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
{
"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.
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;
});
É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 :
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:
})
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.
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)
...
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:
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);
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 :
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:
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()
85
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('The first promise has resolved');
resolve(10);
}, 1 * 1000);
});
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 :
});
});
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');
}
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));
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:
93
Erreurs à l’intérieur des promesses
• Nous modifions la fonction getUserById() pour générer une erreur dans la
promesse :
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));
96
Appel de la fonction reject()
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}`);
}
99
let authorized = false;
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);
}
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
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:
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);
});
}
showServiceCost();
Outre les fonctions régulières, vous pouvez utiliser le mot clé async dans les
expressions de fonction :
let sayHi = async function () {
return 'Hi';
}
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'));
}
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