I Introduction
Zoom sur le javascript concernant les promesses. Arrivée depuis 2017, cette manière d’écrire du code asynchrone devient de plus en plus utilisé.
Quelques liens utiles :
- Pages web :
- vidéos :
- les promesses : https://www.youtube.com/watch?v=SSYt7C4sCbw (juste les promesses)
- la totale : https://www.grafikart.fr/tutoriels/promise-async-await-875
II Les callback
Pour faire du code asynchrone, il y a les callback.
Exemple :
// simple fonction utilisant un callback
function affiche_differe(temps_ms,callback) {
setTimeout(()=>{
callback()
},temps_ms)
}
// Main :
affiche_differe(5000, ()=> {
console.log("Texte écrit au bout de 5000ms d'attente")
})
console.log("Ce texte sera affiché avant ...")
La fonction « affiche_differe » affiche un texte au bout de 5000ms via la méthode de callback.
II Les promesses
Les promesses sont des objets instanciés de la classe Promise. Elle prend en paramètre une fonction de callback utilisant 2 paramètres : resolve et reject
let ma_promesse = new Promise((resolve,reject)=>{
})
Ainsi notre exemple pourra ressembler à celui-ci :
let affiche_differe2 = new Promise((resolve,reject)=>{
let temps_ms = 5000
setTimeout(()=>{
console.log("Texte affiché après " + temps_ms + " ms")
},temps_ms)
})
Mais 2 petits problèmes à résoudre :
- avoir récupérer le callback
- avoir ses propres paramètres d’appel : ici il n’y en pas car c’est un object promise que l’on instancie.
Ainsi le nouveau code pour l’utilisation du call back : utiliser la méthode « then » pour récupérer le callback du « resolve » et « catch » pour le callback du « reject » :
let affiche_differe2 = new Promise((resolve,reject)=>{
let temps_ms = 5000
if (temps_ms) {
setTimeout(()=>{
resolve(temps_ms)
},temps_ms)
} else {
reject ("Erreur : pas de temps défini")
}
})
// Main :
affiche_differe2.then((temps_ms)=>{
console.log("Texte affiché après " + temps_ms + " ms")
}).catch((err)=>{
console.log(err)
})
Maintenant il reste à définir ses paramètres (durée 5000ms). La solution est d’utiliser une fonction qui retournera une promesse :
function affiche_differe_final(temps_ms) {
// création d'une promesse
let une_promesse = new Promise((resolve,reject)=>{
if (temps_ms) {
setTimeout(()=>{
resolve(temps_ms)
},temps_ms)
} else {
reject ("Erreur : pas de temps défini")
}
})
// renvoi de la promesse
return une_promesse
}
// Main :
// ici affiche_differe_final est une promesse : définition des méthodes then et catch
affiche_differe_final(5000).then((temps_ms)=>{
console.log("Texte affiché après " + temps_ms + " ms")
}).catch((err)=>{
console.log(err)
})
console.log("Ce texte sera affiché avant ...")
III Async et await
III.1 Async
L’écriture de fonctions utilisant les promesses donne un code assez verbeux. Le fait d’utiliser Async et await va permettre de l’etre moins et de lire code come s’il était synchrone…
Une fonction définie avec le mot clef « Async » renvoie obligatoirement une promesse. Le code ci-dessous permet de comprendre la philosophie :
async function fonctionAsynchroneOk() {
// équivaut à :
// return Promise.resolve('résultat');
return 'résultat';
}
fonctionAsynchroneOk().then(console.log) // log "résultat"
async function fonctionAsynchroneKo() {
// équivaut à :
// return Promise.reject(new Error('erreur'));
throw new Error('erreur');
}
fonctionAsynchroneKo().catch(err => console.log(err.message)) // log "erreur"
Donc le mot clef « Async » permet de ne plus déclarer une promesse dans la fonction et de ne plus faire référence à une méthode callback « resolve() » ou « reject() ». En revanche, l’utilisation de la fonction écrite nécessite d’utiliser les callback « then » et « catch ».
Ce qui, en reprenant les exemples ci-dessus, peut donner le code suivant :
async function affiche_differe3(texte,temps_ms) {
setTimeout(()=>{
console.log(texte)
}, temps_ms)
}
affiche_differe3("Texte affiché après " + 5000 + "ms", 5000 )
console.log("Ce texte sera affiché avant ...")
Attention, l’utilisation du setTimeout peut amèner de la confusion :
async function affiche_differe3(texte,temps_ms) {
setTimeout(()=>{
console.log(texte)
}, temps_ms)
}
affiche_differe3("Texte affiché après " + 5000 + "ms", 5000 ).then(()=>{console.log("fin")})
console.log("Ce texte sera affiché avant ...")
Dans ce cas il sera affiché en premier :
- « Ce texte sera affiché avant…’ ;
- « fin » ;
- « Texte affiché après 5000ms » ;
III.2 Await
C’est le point le plus intéressant. Tout d’abord le mot clef « await » s’utilise uniquement dans une fonction ayant le mot clef « Async ».
Le mot clef await permet :
- d’attendre la résolution d’une promesse
- de retourner le contenu de « resolve() » du return d’une fonction async. (rappel : le return d’une fonction async renvoie une promesse)
Exemple :
async function chiffre_alea(nombre) {
return Math.random() * nombre
}
async function affiche_total() {
let chiffre1 = await chiffre_alea(10)
console.log ("chiffre 1 : " + chiffre1)
let chiffre2 = await chiffre_alea(10)
console.log ("chiffre 2 : " + chiffre2)
let chiffre3 = await chiffre_alea(10)
console.log ("chiffre 3 : " + chiffre3)
let somme = chiffre1 + chiffre2 + chiffre3
console.log ("La somme est :" + somme)
}
affiche_total()
Ce qui permet d’exécuter du code asynchrone séquentiellement (et d’avoir l’impression de lire du code synchrone).
Pour la gestion des erreurs, il suffira d’encapsuler le code d’un try/catch comme suivant :
async function chiffre_alea(nombre) {
let valeur = Math.random() * nombre
if (valeur < 0.5) throw new Error('erreur chiffre_alea : chiffre trop petit !!!');
return valeur
}
async function affiche_total() {
try {
let chiffre1 = await chiffre_alea(10)
console.log ("chiffre 1 : " + chiffre1)
let chiffre2 = await chiffre_alea(10)
console.log ("chiffre 2 : " + chiffre2)
let chiffre3 = await chiffre_alea(10)
console.log ("chiffre 3 : " + chiffre3)
let somme = chiffre1 + chiffre2 + chiffre3
console.log ("La somme est :" + somme)
} catch (erreur) {
console.log(erreur)
}
}
affiche_total()
ce qui donne le résultat suivant :
chiffre 1 : 5.422324576298465
chiffre 2 : 7.463496462045109
chiffre 3 : 8.638824117224802
La somme est :21.524645155568376
ou celui-ci en cas d’erreur :
chiffre 1 : 4.832829568647341
Error: erreur chiffre_alea : chiffre trop petit !!!
at chiffre_alea (C:\nodejs_www\test7_msql2\callback.js:30:26)
at affiche_total (C:\nodejs_www\test7_msql2\callback.js:40:24)
at processTicksAndRejections (internal/process/task_queues.js:97:5)