L’enfer des callbacks
-
Upload
aurelien-bourdon -
Category
Engineering
-
view
319 -
download
0
Transcript of L’enfer des callbacks
L’enfer des callbacksUn cas d’étude en *(Node)JS
Aurélien Bourdon@aurelienbourdon
describe('.totalValue', function() {
it('should calculate the total value of items in a space', function(done) {
var table = new Item('table', 'dining room', '07/23/2014', '1', '3000');
var chair = new Item('chair', 'living room', '07/23/2014', '3', '300');
var couch = new Item('couch', 'living room', '07/23/2014', '2', '1100');
var chair2 = new Item('chair', 'living room', '07/23/2014', '4', '500');
var bed = new Item('bed', 'dining room', '07/23/2014', '1', '2000');
table.save(function() {
chair.save(function() {
couch.save(function() {
chair2.save(function() {
bed.save(function() {
Item.totalValue({room: 'dining room'}, function(totalValue) {
expect(totalValue).to.equal(5000); done();
});
});
});
});
});
});
});
});Sources
http://thecodebarbarian.com/2015/03/20/callback-hell-is-a-mythhttps://atdreamstate.wordpress.com/2014/12/26/hell-hellfire/
Pourquoi est-ce l’enfer ?
Pourquoi est-ce l’enfer ?
Lecture en cascade
Pourquoi est-ce l’enfer ?
Lecture en cascade
Absence de description fonctionnelle
Pourquoi est-ce l’enfer ?
Lecture en cascade
Couplage fort entre les fonctions
Absence de description fonctionnelle
Pourquoi est-ce l’enfer ?
Lecture en cascade
Lisibilité hardue
Maintenance difficile
Sourcehttp://memesvault.com/sad-meme-face/
Absence de description fonctionnelle
Couplage fort entre les fonctions
Cas d’étude
function parseCurrentDir() {
fs.readdir('.', function (err, files) {
if (err) {
console.error(err);
} else {
files.forEach(function (file) {
fs.readFile(file, 'utf8', function (err, content) {
if (err) {
console.error(err);
} else {
console.log(content);
}
});
});
}
});
}
#1 Modularisation
Modularisation ?
Nommage et externalisation des fonctions
function parseCurrentDir() {
fs.readdir('.', function (err, files) {
if (err) {
console.error(err);
} else {
files.forEach(function (file) {
fs.readFile(file, 'utf8', function (err, content) {
if (err) {
console.error(err);
} else {
console.log(content);
}
});
});
}
});
}
function parseCurrentDir() {
fs.readdir('.', function readFiles(err, files) {
if (err) {
console.error(err);
} else {
files.forEach(function readFile(file) {
fs.readFile(file, 'utf8', function printContent(err, content) {
if (err) {
console.error(err);
} else {
console.log(content);
}
});
});
}
});
}
function readFile(file) {
fs.readFile(file, 'utf8', printContent);
}
function printContent(err, content) {
if (err) {
console.error(err);
} else {
console.log(content);
}
}
function parseCurrentDir() {
fs.readdir('.', readFiles);
}
function readFiles(err, files) {
if (err) {
console.error(err);
} else {
files.forEach(readFile);
}
}
function readFile(file) {
fs.readFile(file, 'utf8', printContent);
}
function printContent(err, content) {
if (err) {
console.error(err);
} else {
console.log(content);
}
}
function parseCurrentDir() {
fs.readdir('.', readFiles);
}
function readFiles(err, files) {
if (err) {
console.error(err);
} else {
files.forEach(readFile);
}
}
#2 Découplage
Découplage ?
Séparation des traitements du flow d’exécution
Découplage ?
Séparation des traitements du flow d’exécution
En utilisant des promises :-)
Promises ?
“Abstraction servant de proxy pour un résultat non-connu au moment où il est référencé pour la
première fois, car son calcul ou son obtention se feront “plus tard” à l’exécution”
- Wikipédia
Promises !
(...)
(...)
Promises !
https://github.com/kriskowal/q
function parseCurrentDir() {
fs.readdir('.', readFiles);
}
function parseCurrentDir() {
fs.readdir('.', function (err, files) {
});
}
function parseCurrentDir() {
fs.readdir('.', function (err, files) {
});
}
function parseCurrentDir() {
Q.Promise(function (resolve, reject, notify) {
fs.readdir('.', function (err, files) {
});
});
}
function parseCurrentDir() {
Q.Promise(function (resolve, reject, notify) {
fs.readdir('.', function (err, files) {
if (err) {
reject(err);
} else {
resolve(files);
}
});
});
}
function parseCurrentDir() {
return Q.Promise(function (resolve, reject, notify) {
fs.readdir('.', function (err, files) {
if (err) {
reject(err);
} else {
resolve(files);
}
});
});
}
function parseCurrentDir() {
return Q.Promise(function (resolve, reject, notify) {
fs.readdir('.', function (err, files) {
if (err) {
reject(err);
} else {
resolve(files);
}
});
});
}
Séparation des traitements du flow d’exécution
readFiles(files);
function listCurrentDirFiles() {
return Q.Promise(function (resolve, reject, notify) {
fs.readdir('.', function (err, files) {
if (err) {
reject(err);
} else {
resolve(files);
}
});
});
}
Séparation des traitements du flow d’exécution
readFiles(files);
“Mais comment lier notre fonction avec la suivante ?”
“Mais comment lier notre fonction avec la suivante ?”
En utilisant le chaînage de promises !
listCurrentDirFiles()
listCurrentDirFiles()
.then(readFiles)
listCurrentDirFiles()
.then(readFiles)
.then(...)
listCurrentDirFiles()
.then(readFiles)
.then(...)
.catch(console.error)
listCurrentDirFiles()
.then(readFiles)
.then(...)
.catch(console.error)
.done()
“Du coup au final ça donne quoi ?”
function parseCurrentDir() {
fs.readdir('.', function (err, files) {
if (err) {
console.error(err);
} else {
files.forEach(function (file) {
fs.readFile(file, 'utf8', function (err, content) {
if (err) {
console.error(err);
} else {
console.log(content);
}
});
});
}
});
}
function parseCurrentDir() {
listCurrentDirFiles()
.then(readFiles)
.then(printContents)
.catch(console.error)
.done();
}
function readFile(file) {
return Q.Promise(function (resolve, reject) {
fs.readFile(file, 'utf8', function (err, content) {
if (err) {
reject(err);
} else {
resolve(content);
}
});
});
}
function printContents(contents) {
contents.forEach(function (content) {
if (content.state === 'fulfilled') {
console.log(content.value);
} else {
console.log('Unable to parse file due to %s', content.reason);
}
});
}
function listCurrentDirFiles() {
return Q.Promise(function (resolve, reject) {
fs.readdir('.', function (err, files) {
if (err) {
reject(err);
} else {
resolve(files);
}
});
});
}
function readFiles(files) {
return Q.allSettled(files.map(readFile));
}
function readFile(file) {
return Q.Promise(function (resolve, reject) {
fs.readFile(file, 'utf8', function (err, content) {
if (err) {
reject(err);
} else {
resolve(content);
}
});
});
}
function printContents(contents) {
contents.forEach(function (content) {
if (content.state === 'fulfilled') {
console.log(content.value);
} else {
console.error(content.reason);
}
});
}
function readFile(file) {
return Q.Promise(function (resolve, reject) {
fs.readFile(file, 'utf8', function (err, content) {
if (err) {
reject(err);
} else {
resolve(content);
}
});
});
}
function printContents(contents) {
contents.forEach(function (content) {
if (content.state === 'fulfilled') {
console.log(content.value);
} else {
console.log('Unable to parse file due to %s', content.reason);
}
});
}
function listCurrentDirFiles() {
return Q.denodeify(fs.readdir)('.');
}
function readFiles(files) {
return Q.allSettled(files.map(readFile));
}
function readFile(file) {
return Q.denodeify(fs.readFile)(file, 'utf8');
}
function printContents(contents) {
contents.forEach(function (content) {
if (content.state === 'fulfilled') {
console.log(content.value);
} else {
console.error(content.reason);
}
});
}
Pour aller plus loin
D’autres approches peuvent être utilisées e.g., Async.JS (https://github.com/caolan/async)
Référenceshttp://callbackhell.com/
http://stackabuse.com/avoiding-callback-hell-in-node-js/
http://thecodebarbarian.com/2015/03/20/callback-hell-is-a-myth
https://github.com/kriskowal/q
https://strongloop.com/strongblog/node-js-callback-hell-promises-generators/
http://colintoh.com/blog/staying-sane-with-asynchronous-programming-promises-and-generators
http://blog.ippon.fr/2015/01/27/les-promesses-en-angularjs-33/
http://complexitymaze.com/2014/03/03/javascript-promises-a-comparison-of-libraries/
https://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/
L’enfer des callbacksUn cas d’étude en *(Node)JS
Aurélien Bourdon@aurelienbourdon