SlideShare une entreprise Scribd logo
1  sur  44
Télécharger pour lire hors ligne
L’enfer des callbacks
Un 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-myth
https://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
Source
http://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érences
http://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 callbacks
Un cas d’étude en *(Node)JS
Aurélien Bourdon
@aurelienbourdon

Contenu connexe

Tendances

OpenResty/Lua 70+ Advanced Programming Skills and Optimization tips
OpenResty/Lua 70+ Advanced Programming Skills and Optimization tipsOpenResty/Lua 70+ Advanced Programming Skills and Optimization tips
OpenResty/Lua 70+ Advanced Programming Skills and Optimization tipsHo Kim
 
Node.JS
Node.JSNode.JS
Node.JSeibaan
 
商派信息安全解决方案
商派信息安全解决方案商派信息安全解决方案
商派信息安全解决方案wanglei999
 
How to stop writing spaghetti code - JSConf.eu 2010
How to stop writing spaghetti code - JSConf.eu 2010How to stop writing spaghetti code - JSConf.eu 2010
How to stop writing spaghetti code - JSConf.eu 2010Tom Croucher
 
MySQLオンラインマイグレーションツールgh-ostで深夜メンテナンスを無くした話
MySQLオンラインマイグレーションツールgh-ostで深夜メンテナンスを無くした話MySQLオンラインマイグレーションツールgh-ostで深夜メンテナンスを無くした話
MySQLオンラインマイグレーションツールgh-ostで深夜メンテナンスを無くした話Shuto Suzuki
 
Handbook - From jQuery to YUI 3
Handbook - From jQuery to YUI 3Handbook - From jQuery to YUI 3
Handbook - From jQuery to YUI 3Ying-Hsiang Liao
 
Introducing Ballerina
Introducing BallerinaIntroducing Ballerina
Introducing BallerinaWSO2
 
Arsip coding java rev1
Arsip coding java rev1Arsip coding java rev1
Arsip coding java rev1Anbu Dumilano
 
Week 7 unit3 (chapter 10-11)
Week 7   unit3 (chapter 10-11)Week 7   unit3 (chapter 10-11)
Week 7 unit3 (chapter 10-11)aj.mapling
 
Cantidad De movimiento Lineal
Cantidad De movimiento LinealCantidad De movimiento Lineal
Cantidad De movimiento LinealJefferson Antamba
 
Assalamualaykum warahmatullahi wabarakatuu
Assalamualaykum warahmatullahi wabarakatuuAssalamualaykum warahmatullahi wabarakatuu
Assalamualaykum warahmatullahi wabarakatuuiswan_di
 
Java Thread Cronometro
Java Thread CronometroJava Thread Cronometro
Java Thread Cronometrojubacalo
 

Tendances (18)

OpenResty/Lua 70+ Advanced Programming Skills and Optimization tips
OpenResty/Lua 70+ Advanced Programming Skills and Optimization tipsOpenResty/Lua 70+ Advanced Programming Skills and Optimization tips
OpenResty/Lua 70+ Advanced Programming Skills and Optimization tips
 
Node.JS
Node.JSNode.JS
Node.JS
 
商派信息安全解决方案
商派信息安全解决方案商派信息安全解决方案
商派信息安全解决方案
 
How to stop writing spaghetti code - JSConf.eu 2010
How to stop writing spaghetti code - JSConf.eu 2010How to stop writing spaghetti code - JSConf.eu 2010
How to stop writing spaghetti code - JSConf.eu 2010
 
MySQLオンラインマイグレーションツールgh-ostで深夜メンテナンスを無くした話
MySQLオンラインマイグレーションツールgh-ostで深夜メンテナンスを無くした話MySQLオンラインマイグレーションツールgh-ostで深夜メンテナンスを無くした話
MySQLオンラインマイグレーションツールgh-ostで深夜メンテナンスを無くした話
 
Handbook - From jQuery to YUI 3
Handbook - From jQuery to YUI 3Handbook - From jQuery to YUI 3
Handbook - From jQuery to YUI 3
 
Introducing Ballerina
Introducing BallerinaIntroducing Ballerina
Introducing Ballerina
 
Arsip coding java rev1
Arsip coding java rev1Arsip coding java rev1
Arsip coding java rev1
 
PHP Profiling
PHP ProfilingPHP Profiling
PHP Profiling
 
Week 7 unit3 (chapter 10-11)
Week 7   unit3 (chapter 10-11)Week 7   unit3 (chapter 10-11)
Week 7 unit3 (chapter 10-11)
 
Cantidad De movimiento Lineal
Cantidad De movimiento LinealCantidad De movimiento Lineal
Cantidad De movimiento Lineal
 
Img 0008
Img 0008Img 0008
Img 0008
 
HVAC Design
HVAC DesignHVAC Design
HVAC Design
 
Assalamualaykum warahmatullahi wabarakatuu
Assalamualaykum warahmatullahi wabarakatuuAssalamualaykum warahmatullahi wabarakatuu
Assalamualaykum warahmatullahi wabarakatuu
 
Секреты WP_Query
Секреты WP_QueryСекреты WP_Query
Секреты WP_Query
 
Yureka
YurekaYureka
Yureka
 
Ejercicios
EjerciciosEjercicios
Ejercicios
 
Java Thread Cronometro
Java Thread CronometroJava Thread Cronometro
Java Thread Cronometro
 

L’enfer des callbacks