Javascript promises
Les promesses
Les promesses
"Une promesse est une abstraction logicielle qui rend plus agréable le fait de travailler avec du code asynchrone"
Domenic Denicola, co-creator of Q
Les promesses
En javascript
- Les promesses sont bien plus anciennes que JS,
- Les promesses sont natives en JavaScript depuis ES6,
- Supportées par Chrome 32, Opera 19, Firefox 29, Safari 8 & Microsoft Edge,
- Il existe un polyfill sinon !
- Les promesses ES6 sont "A+ compliant"
Specification (+ compliance test) : promisesaplus.com
Promise/A+
Implémentations
Autres librairies compatibles Promise/A+ :
L'asynchrone ?
Callback hell ?
Sans promesses
"Callback hell" 😱
Pyramid of Doom
Avec des promesses
C'est déjà plus lisible ! 🙂
var floppy = require('floppy');
floppy.load('disk1')
.then((data1) => floppy.prompt('Please insert disk 2'))
.then(() => floppy.load('disk2'))
.then((data2) => floppy.prompt('Please insert disk 3'))
.then(() => floppy.load('disk3'))
.then((data3) => floppy.prompt('Please insert disk 4'))
.then(() => floppy.load('disk4'))
.then((data4) => floppy.prompt('Please insert disk 5'))
.then(() => floppy.load('disk5'))
.then((data4) => floppy.prompt('OK!'));
Si floppy.load() et floppy.prompt() retournent une promesse
Pour simplifier
floppy.load("disk1", function (err, results) {
// the rest of your code goes here.
});
floppy.load("disk1").then(function() {
// the rest of your code goes here.
});
devient (si load() retourne un "then-able") :
Sans promesse :
Coder un "thenable" simple !
Terminologie
- fulfilled - L'action associée à la promesse a réussi.
- rejected - L'action associée à la promesse a échoué.
- pending - N'a pas encore réussi ou échoué.
- settled - A réussi ou échoué.
Déclarer une promesse (ES6)
Resolve & Reject
floppy.load = new Promise(function(resolve, reject) {
// faire un truc, peut-être asynchrone, puis…
if (/* tout a bien marché */) {
resolve("Ces trucs ont marché !"); // <== FULFILLED
}
else {
reject(Error("Ça a foiré")); // <== REJECTED
}
});
Déclarer une promesse (ES6)
Déclaration :
Utilisation :
getJSON('story.json').then(function getChapter1(story) {
// [...] display chapter 1 & returns a promise
}).then(function getChapter2() {
// [...] display chapter 2 & returns a promise
}).then(function getChapter3() {
// [...] display chapter 3
})
Sequencing: "chainer les then"
Chainer les "then"
/**
* Generates a promise adding 10 to a value after 1 sec
*/
function getPromise(value) {
return new Promise(function(resolve, reject) {
setTimeout(function() { resolve(value + 10); }, 1000);
});
}
/**
* Promises sequence
*/
getPromise(0)
.then(result => getPromise(result))
.then(result => getPromise(result))
.then(result => {
console.log(result);
});
Que vaut "result" à la fin ?
cf. http://bevacqua.github.io/promisees
Attraper les erreurs
catch
new Promise(function(resolve, reject) {
setTimeout(function() { reject('ERR!'); }, 3000);
})
.then(function(e) { console.log('done', e); })
.catch(function(e) { console.log('catch: ', e); });
// From the console:
// 'catch: ERR!'
cf. http://bevacqua.github.io/promisees
Errors chaining
var log = "";
function doWork() {
log += "W";
return Promise.resolve();
}
function doError() {
log += "E";
throw new Error("oops!");
}
function errorHandler(error) {
log += "H";
}
doWork()
.then(doWork)
.then(doError)
.then(doWork)
.then(doWork, errorHandler)
.then(verify);
function verify() {
console.log(log);
done();
}
1) Lors d'une exception, les "then" suivants ne sont pas exécutés jusqu'à tomber sur un "catch".
2) Après un "catch", on reprend la chaine classique (on repasse dans le "then").
Que vaut "log" ?
En parallèle
var request1 = fetch('/users.json');
var request2 = fetch('/articles.json');
Promise.all([request1, request2]).then(function(results) {
// Both promises done!
});
Promise.all
getJSON('story.json').then(function(story) {
return Promise.all([getChapter1(), getChapter2(), getChapter3()])
}).then(function(results) {
// Display chapter 1, 2 & 3
});
En parallèle
const p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "one");
});
const p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, "two");
});
const p3 = new Promise((resolve, reject) => {
setTimeout(resolve, 3000, "three");
});
Promise.all([p1, p2, p3]).then(value => {
console.log(value);
}, reason => {
console.log(reason)
});
En parallèle
const p1 = new Promise(function(resolve, reject) {
setTimeout(resolve, 500, "one");
});
const p2 = new Promise(function(resolve, reject) {
setTimeout(resolve, 100, "two");
});
Promise.race([p1, p2]).then(function(value) {
console.log(value); // "two"
// Both resolve, but p2 is faster
});
Et aussi, "Promise.race"
On récupère ainsi la première qui est "fulfilled"
Le futur ?
https://pouchdb.com/2015/03/05/taming-the-async-beast-with-es7.html
Le futur !
getArticleId()
.then(id => getArticleContent(id))
.then(content => console.log(content));
getArticleId() et getArticleContent() retournent une Promise
Sachant que :
avec Async/Await
Avec une séquence de "then" :
// Await ne marchera que dans une fonction marquée async
async function getMyArticle() {
const id = await getArticleId();
const content = await getArticleContent(id);
console.log(content);
return content;
}
Avec async/await :
Chaining ?
avec Async/Await
async function getMyArticle() {
const id = await getArticleId();
const content = await getArticleContent(id);
return content;
}
On peut l'apeller avec un autre async
async function read() {
var content = await getMyArticle();
console.log(content);
}
Ou l'utiliser comme une promesse
getMyArticle().then(content => console.log(content));
Concurrency ?
avec Async/Await
async function concurrent () {
const [r1, r2, r3] = await Promise.all([p1, p2, p3]);
}
Errors catching ?
avec Async/Await
async function getMyArticle() {
const id = await getArticleId();
if (id === null) {
throw 'err';
} else {
return content;
}
}
Déjà vu ?
async function read() {
try {
var content = await getMyArticle();
} catch (err) {
console.error(err);
}
}
Quand ?
Async/Await
- Stage 4 ("Finished") ,
- Attendu pour ECMA2017,
- Finished proposals.
Promises
exercice, vite fait !
Avec Babel REPL, écrire une suite de promesses pour :
- récupérer les acteurs d'un film sur http://www.omdbapi.com
- attendre 2 secondes
- afficher les acteurs triés par ordre alphabétique.
Javascript promises
By Julien Herpin
Javascript promises
- 1,310