SlideShare a Scribd company logo
Avoiding Callback Hell with
Async.js

C. Aaron Cois, Ph.D.

www.codehenge.net
Sup

www.codehenge.net

@aaroncois
github.com/cacois
So, JavaScript?
What’s cool?
• Robust event model
• Asynchronous programming
• Client-side and Server-side
What’s cool?
• Robust event model
• Asynchronous programming
• Client-side and Server-side
What can cause headaches?
Callbacks

JavaScript uses callback functions to
handle asynchronous control flow
Anatomy of a Callback
fs = require('fs');
fs.readFile('f1.txt','utf8',function(err,data){
if (err) {
return console.log(err);
}
console.log(data);
});
Anatomy of a Callback
fs = require('fs');
fs.readFile('f1.txt','utf8',function(err,data){
if (err) {
return console.log(err);
}
console.log(data);
});

Anonymous, inline callback
Anatomy of a Callback
fs = require('fs');
fs.readFile('f1.txt','utf8',
function(err,data){
if (err) {
return console.log(err);
}
console.log(data);
}
);

Equivalent
formatting
Callback Hell

When working with callbacks, nesting
can get quite out of hand…
Callback Hell
func1(param, function(err, res) {
func2(param, function(err, res) {
func3(param, function(err, res) {
func4(param, function(err, res) {
func5(param, function(err, res) {
func6(param, function(err, res) {
func7(param, function(err, res) {
func8(param, function(err, res) {
func9(param, function(err, res) {
// Do something…
});
});
});
});
});
});
});
});
});
Callback Hell
func1(param, function(err, res) {
func2(param, function(err, res) {
func3(param, function(err, res) {
func4(param, function(err, res) {
func5(param, function(err, res) {
func6(param, function(err, res) {
func7(param, function(err, res) {
func8(param, function(err, res) {
func9(param, function(err, res) {
// Do something…
});
});
});
});
});
});
});
});
});
Best case, this is linear
func1

func2

func3

...

func9
But it can branch
func1

func2

func2

func3

func3

func3

func3

...

...

...

...

func9

func9

func9

func9
But it can branch
…
func2(param, function(err, results) {
_.each(results, func3(param, function(err, res) {
func4(param, function(err, res) {
…
});
}
});
});

func1

func2

func2

func3

func3

func3

func3

...

...

...

...

func9

func9

func9

func9
Some specific challenges
• When branching, we can’t know the
order of these function calls
• If we want parallel execution, we have
to do some real gymnastics to get
return data back together
• Also, scoping becomes a challenge
A more ‘real’ example
var db = require('somedatabaseprovider');
//get recent posts
http.get('/recentposts', function(req, res) {
// open database connection
db.openConnection('host', creds,function(err, conn){
res.param['posts'].forEach(post) {
conn.query('select * from users where
id='+post['user'],function(err,users){
conn.close();
res.send(users[0]);
});
}
});
});
A more ‘real’ example
var db = require('somedatabaseprovider');
//get recent posts
http.get('/recentposts', function(req, res) {
// open database connection
db.openConnection('host', creds,function(err, conn){
res.param['posts'].forEach(post) {
conn.query('select * from users where
id='+post['user'],function(err,users){
conn.close();
res.send(users[0]);
});
}
});
});
Solutions
• You can make this easier to read by
separating anonymous functions
• Passing function references instead of
anonymous functions helps even more
Inline callback
fs = require('fs');
fs.readFile('f1.txt','utf8',function(err,data){
if (err) {
return console.log(err);
}
console.log(data);
});
Separate Callback
fs = require('fs');
callback = function(err,data){
if (err) {
return console.log(err);
}
console.log(data);
}
fs.readFile('f1.txt','utf8',callback);
Can turn this:
var db = require('somedatabaseprovider');

http.get('/recentposts', function(req, res){
db.openConnection('host', creds, function(err,
conn){
res.param['posts'].forEach(post) {
conn.query('select * from users where id=' +
post['user'],function(err,results){
conn.close();
res.send(results[0]);
});
}
});
});
…into this
var db = require('somedatabaseprovider');

http.get('/recentposts', afterRecentPosts);
function afterRecentPosts(req, res) {
db.openConnection('host', creds, function(err, conn) {
afterDBConnected(res, conn);
});
}
function afterDBConnected(err, conn) {
res.param['posts'].forEach(post) {
conn.query('select * from users where id='+post['user'],afterQuery);
}
}
function afterQuery(err, results) {
conn.close();
res.send(results[0]);
}
Good start!
• Callback function separation is a nice
aesthetic fix
• The code is more readable, and thus
more maintainable
• But it doesn’t improve your control flow
– Branching and parallel execution are still
problems
Enter Async.js
Async.js provides common patterns for
asyncronous code control flow
https://github.com/caolan/async
BONUS: Also provides some common
functional programming paradigms
Client or Server -side
My examples will mostly be Node.js
code, but Async.js can be used in both
client and server side code
Serial/Parallel Execution
Run functions in series…
Function
1

Function
2

Function
3

Function
4

Function
2

Function
3

Function
4

…or parallel
Function
1
Serial/Parallel Functions
async.parallel([
function(){ ... },
function(){ ... }
], callback);
async.series([
function(){ ... },
function(){ ... }
]);
Serial/Parallel Functions
async.parallel([
function(){ ... },
function(){ ... }
], callback);
async.series([
function(){ ... },
function(){ ... }
]);

Single Callback!
Waterfall Execution
Async also provides a flow for serial
execution, passing results to successive
functions

Function
1

args

args

args
Function
2

Function
3

Function
4
Waterfall
async.waterfall([
function(){ callback(arg1); },
function(arg1) {
callback(ar2,ar3)
},
function(arg1, arg2){
callback(“done”)
}
], function(err, results){
// results now equals “done”
});
Times
Times() offers a shortcut to iterating over
a function multiple times in parallel
async.times(5, function(n, next) {
createUser(n,function(err, user {
next(err, user);
})
}, function(err, users){
// „users‟ now contains 5 users
});
Let’s see some code…
Collection Management
Functional programming provides some
useful tools that are becoming
mainstream

Specifically, map, reduce, and filter
operations are now common in many
languages
Map
The Map operation calls a given
function on each item of a list
Map
async.map([„file1‟,„file2‟,„file3‟],
funct,
callback);
async.mapSeries([„file1‟,„file2‟],
funct,
callback);

async.mapLimit([„file1‟,„file2‟,„file3‟],
limit,
funct,
callback);
Reduce
The Reduce operation aggregates a list
of values into a single result, using a
specified aggregation function
Reduce

Initial result state

async.reduce([val1,val2,…],memo,
function(memo,item,cb){
// doStuff
});
async.reduceRight([val1,val2],memo,
function(memo,item,cb){
// doStuff
});
Filter
To minimize computational overhead,
it’s often helpful to filter data sets to only
operate on acceptable values

Async.js provides a Filter function to do
just this
Filter
async.filter([„file1‟,„file2‟,„file3‟],
fs.exists,
function(results){
// results is now an array
// of files that exist
});
In Summation
Async.js provides:
• Powerful asynchronous control flows
• Functional programming tools

Write clean code, live the dream.
Thanks!

Any questions?

@aaroncois
Ad

Recommended

Callbacks and control flow in Node js
Callbacks and control flow in Node js
Thomas Roch
 
Callbacks, promises, generators - asynchronous javascript
Callbacks, promises, generators - asynchronous javascript
Łukasz Kużyński
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.js
Piotr Pelczar
 
Avoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promises
Ankit Agarwal
 
Understanding Asynchronous JavaScript
Understanding Asynchronous JavaScript
jnewmanux
 
Javascript Promises/Q Library
Javascript Promises/Q Library
async_io
 
Promises, Promises
Promises, Promises
Domenic Denicola
 
The Promised Land (in Angular)
The Promised Land (in Angular)
Domenic Denicola
 
$q and Promises in AngularJS
$q and Promises in AngularJS
a_sharif
 
Promise pattern
Promise pattern
Sebastiaan Deckers
 
JavaScript Promise
JavaScript Promise
Joseph Chiang
 
JavaScript Promises
JavaScript Promises
L&T Technology Services Limited
 
Introduction to reactive programming & ReactiveCocoa
Introduction to reactive programming & ReactiveCocoa
Florent Pillet
 
The evolution of asynchronous javascript
The evolution of asynchronous javascript
Alessandro Cinelli (cirpo)
 
Avoiding callback hell with promises
Avoiding callback hell with promises
TorontoNodeJS
 
JavaScript Promises
JavaScript Promises
Tomasz Bak
 
Getting Comfortable with JS Promises
Getting Comfortable with JS Promises
Asa Kusuma
 
JavaScript Promises
JavaScript Promises
Derek Willian Stavis
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3
Luciano Mammino
 
JavaScript promise
JavaScript promise
eslam_me
 
Practical JavaScript Promises
Practical JavaScript Promises
Asa Kusuma
 
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Islam Sharabash
 
Asynchronous Programming FTW! 2 (with AnyEvent)
Asynchronous Programming FTW! 2 (with AnyEvent)
xSawyer
 
CLS & asyncListener: asynchronous observability for Node.js
CLS & asyncListener: asynchronous observability for Node.js
Forrest Norvell
 
Serverless, The Middy Way - Workshop
Serverless, The Middy Way - Workshop
Luciano Mammino
 
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Luciano Mammino
 
Java Script Promise
Java Script Promise
Alok Guha
 
Middy.js - A powerful Node.js middleware framework for your lambdas​
Middy.js - A powerful Node.js middleware framework for your lambdas​
Luciano Mammino
 
Node js
Node js
LearningTech
 
Flow control in node.js
Flow control in node.js
iamhjoo (송형주)
 

More Related Content

What's hot (20)

$q and Promises in AngularJS
$q and Promises in AngularJS
a_sharif
 
Promise pattern
Promise pattern
Sebastiaan Deckers
 
JavaScript Promise
JavaScript Promise
Joseph Chiang
 
JavaScript Promises
JavaScript Promises
L&T Technology Services Limited
 
Introduction to reactive programming & ReactiveCocoa
Introduction to reactive programming & ReactiveCocoa
Florent Pillet
 
The evolution of asynchronous javascript
The evolution of asynchronous javascript
Alessandro Cinelli (cirpo)
 
Avoiding callback hell with promises
Avoiding callback hell with promises
TorontoNodeJS
 
JavaScript Promises
JavaScript Promises
Tomasz Bak
 
Getting Comfortable with JS Promises
Getting Comfortable with JS Promises
Asa Kusuma
 
JavaScript Promises
JavaScript Promises
Derek Willian Stavis
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3
Luciano Mammino
 
JavaScript promise
JavaScript promise
eslam_me
 
Practical JavaScript Promises
Practical JavaScript Promises
Asa Kusuma
 
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Islam Sharabash
 
Asynchronous Programming FTW! 2 (with AnyEvent)
Asynchronous Programming FTW! 2 (with AnyEvent)
xSawyer
 
CLS & asyncListener: asynchronous observability for Node.js
CLS & asyncListener: asynchronous observability for Node.js
Forrest Norvell
 
Serverless, The Middy Way - Workshop
Serverless, The Middy Way - Workshop
Luciano Mammino
 
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Luciano Mammino
 
Java Script Promise
Java Script Promise
Alok Guha
 
Middy.js - A powerful Node.js middleware framework for your lambdas​
Middy.js - A powerful Node.js middleware framework for your lambdas​
Luciano Mammino
 
$q and Promises in AngularJS
$q and Promises in AngularJS
a_sharif
 
Introduction to reactive programming & ReactiveCocoa
Introduction to reactive programming & ReactiveCocoa
Florent Pillet
 
Avoiding callback hell with promises
Avoiding callback hell with promises
TorontoNodeJS
 
JavaScript Promises
JavaScript Promises
Tomasz Bak
 
Getting Comfortable with JS Promises
Getting Comfortable with JS Promises
Asa Kusuma
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3
Luciano Mammino
 
JavaScript promise
JavaScript promise
eslam_me
 
Practical JavaScript Promises
Practical JavaScript Promises
Asa Kusuma
 
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Node.js: Continuation-Local-Storage and the Magic of AsyncListener
Islam Sharabash
 
Asynchronous Programming FTW! 2 (with AnyEvent)
Asynchronous Programming FTW! 2 (with AnyEvent)
xSawyer
 
CLS & asyncListener: asynchronous observability for Node.js
CLS & asyncListener: asynchronous observability for Node.js
Forrest Norvell
 
Serverless, The Middy Way - Workshop
Serverless, The Middy Way - Workshop
Luciano Mammino
 
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Luciano Mammino
 
Java Script Promise
Java Script Promise
Alok Guha
 
Middy.js - A powerful Node.js middleware framework for your lambdas​
Middy.js - A powerful Node.js middleware framework for your lambdas​
Luciano Mammino
 

Similar to Avoiding Callback Hell with Async.js (20)

Node js
Node js
LearningTech
 
Flow control in node.js
Flow control in node.js
iamhjoo (송형주)
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Domenic Denicola
 
Playing With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.js
Mike Hagedorn
 
node.js workshop- JavaScript Async
node.js workshop- JavaScript Async
Qiong Wu
 
Node.js Patterns for Discerning Developers
Node.js Patterns for Discerning Developers
cacois
 
Event Driven Javascript
Event Driven Javascript
Federico Galassi
 
Event driven javascript
Event driven javascript
Francesca1980
 
Event driven javascript
Event driven javascript
Francesca1980
 
Asynchronous programming with java script and node.js
Asynchronous programming with java script and node.js
Timur Shemsedinov
 
All you need to know about Callbacks, Promises, Generators
All you need to know about Callbacks, Promises, Generators
Brainhub
 
The art of concurrent programming
The art of concurrent programming
Iskren Chernev
 
Kamil witecki asynchronous, yet readable, code
Kamil witecki asynchronous, yet readable, code
Kamil Witecki
 
Node js
Node js
hazzaz
 
Node.js - Best practices
Node.js - Best practices
Felix Geisendörfer
 
Async discussion 9_29_15
Async discussion 9_29_15
Cheryl Yaeger
 
Douglas Crockford: Serversideness
Douglas Crockford: Serversideness
WebExpo
 
node.js: Javascript's in your backend
node.js: Javascript's in your backend
David Padbury
 
FITC - Node.js 101
FITC - Node.js 101
Rami Sayar
 
Intro to Asynchronous Javascript
Intro to Asynchronous Javascript
Garrett Welson
 
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Domenic Denicola
 
Playing With Fire - An Introduction to Node.js
Playing With Fire - An Introduction to Node.js
Mike Hagedorn
 
node.js workshop- JavaScript Async
node.js workshop- JavaScript Async
Qiong Wu
 
Node.js Patterns for Discerning Developers
Node.js Patterns for Discerning Developers
cacois
 
Event driven javascript
Event driven javascript
Francesca1980
 
Event driven javascript
Event driven javascript
Francesca1980
 
Asynchronous programming with java script and node.js
Asynchronous programming with java script and node.js
Timur Shemsedinov
 
All you need to know about Callbacks, Promises, Generators
All you need to know about Callbacks, Promises, Generators
Brainhub
 
The art of concurrent programming
The art of concurrent programming
Iskren Chernev
 
Kamil witecki asynchronous, yet readable, code
Kamil witecki asynchronous, yet readable, code
Kamil Witecki
 
Node js
Node js
hazzaz
 
Async discussion 9_29_15
Async discussion 9_29_15
Cheryl Yaeger
 
Douglas Crockford: Serversideness
Douglas Crockford: Serversideness
WebExpo
 
node.js: Javascript's in your backend
node.js: Javascript's in your backend
David Padbury
 
FITC - Node.js 101
FITC - Node.js 101
Rami Sayar
 
Intro to Asynchronous Javascript
Intro to Asynchronous Javascript
Garrett Welson
 
Ad

More from cacois (6)

Devopssecfail
Devopssecfail
cacois
 
Machine Learning for Modern Developers
Machine Learning for Modern Developers
cacois
 
Hadoop: The elephant in the room
Hadoop: The elephant in the room
cacois
 
High-Volume Data Collection and Real Time Analytics Using Redis
High-Volume Data Collection and Real Time Analytics Using Redis
cacois
 
Automate your Development Environments with Vagrant
Automate your Development Environments with Vagrant
cacois
 
Node.js: A Guided Tour
Node.js: A Guided Tour
cacois
 
Devopssecfail
Devopssecfail
cacois
 
Machine Learning for Modern Developers
Machine Learning for Modern Developers
cacois
 
Hadoop: The elephant in the room
Hadoop: The elephant in the room
cacois
 
High-Volume Data Collection and Real Time Analytics Using Redis
High-Volume Data Collection and Real Time Analytics Using Redis
cacois
 
Automate your Development Environments with Vagrant
Automate your Development Environments with Vagrant
cacois
 
Node.js: A Guided Tour
Node.js: A Guided Tour
cacois
 
Ad

Recently uploaded (18)

EDSA Revolution and Democracy - the Youth and Students in Focus [uploadable]....
EDSA Revolution and Democracy - the Youth and Students in Focus [uploadable]....
ssuser0e98111
 
Pathos Communications When Reputation Management Becomes Reputation Destructi...
Pathos Communications When Reputation Management Becomes Reputation Destructi...
nathaniel leonard
 
HOW TO ELIMINATE THE EVILS AGAINST HUMANITY CAUSED BY CAPITALISM THROUGHOUT H...
HOW TO ELIMINATE THE EVILS AGAINST HUMANITY CAUSED BY CAPITALISM THROUGHOUT H...
Faga1939
 
PRESS-RELEASE-WIFE-3.pdfKen Ofori-Atta undergoes successful prostate cancer s...
PRESS-RELEASE-WIFE-3.pdfKen Ofori-Atta undergoes successful prostate cancer s...
businessweekghana
 
1716512823916_Sectors_of_Indian_Economy_X.pptx
1716512823916_Sectors_of_Indian_Economy_X.pptx
kavishtiwari2009
 
Air India Plane Crash: Tragedy Strikes Near Ahmedabad
Air India Plane Crash: Tragedy Strikes Near Ahmedabad
New India Abroad
 
Οι χώρες της Ευρώπης με την καλύτερη ποιότητα υδάτων για κολύμβηση – Η θέση τ...
Οι χώρες της Ευρώπης με την καλύτερη ποιότητα υδάτων για κολύμβηση – Η θέση τ...
Newsroom8
 
Arab Spring. protect against corrupt leaders of arab
Arab Spring. protect against corrupt leaders of arab
ShukrullahBaloch
 
INDIAN VACCINE DIPLOMACY: VACCINE MAITRI
INDIAN VACCINE DIPLOMACY: VACCINE MAITRI
DrKAPILKUMARGOEL
 
Kerala a hotspot for ISIS recruitment.pdf
Kerala a hotspot for ISIS recruitment.pdf
Aaryan Kansari
 
the pay postcode lottery event slides.pdf
the pay postcode lottery event slides.pdf
ResolutionFoundation
 
TCS to set up IT facility housing 10k employees in Vizag: Nara Lokesh
TCS to set up IT facility housing 10k employees in Vizag: Nara Lokesh
narsireddynannuri1
 
UPSC Officers News & Insights: Who’s Moving Where in Indian Administration.pdf
UPSC Officers News & Insights: Who’s Moving Where in Indian Administration.pdf
harshitbaisla73
 
Gatwick AG About A Company Shut Down by UK Courts Still Sells Offshore Servic...
Gatwick AG About A Company Shut Down by UK Courts Still Sells Offshore Servic...
nathaniel leonard
 
Connecting the Indian Community Abroad – New India Abroad
Connecting the Indian Community Abroad – New India Abroad
New India Abroad
 
Vijay Rupani Biography: Early Life, Education, and Political Career
Vijay Rupani Biography: Early Life, Education, and Political Career
VoterMood
 
Indian Community Centers – New India Abroad
Indian Community Centers – New India Abroad
New India Abroad
 
POLITICAL AND RELIGIOUS ASPECTS OF POWER IN INDIA
POLITICAL AND RELIGIOUS ASPECTS OF POWER IN INDIA
Anushka Singh
 
EDSA Revolution and Democracy - the Youth and Students in Focus [uploadable]....
EDSA Revolution and Democracy - the Youth and Students in Focus [uploadable]....
ssuser0e98111
 
Pathos Communications When Reputation Management Becomes Reputation Destructi...
Pathos Communications When Reputation Management Becomes Reputation Destructi...
nathaniel leonard
 
HOW TO ELIMINATE THE EVILS AGAINST HUMANITY CAUSED BY CAPITALISM THROUGHOUT H...
HOW TO ELIMINATE THE EVILS AGAINST HUMANITY CAUSED BY CAPITALISM THROUGHOUT H...
Faga1939
 
PRESS-RELEASE-WIFE-3.pdfKen Ofori-Atta undergoes successful prostate cancer s...
PRESS-RELEASE-WIFE-3.pdfKen Ofori-Atta undergoes successful prostate cancer s...
businessweekghana
 
1716512823916_Sectors_of_Indian_Economy_X.pptx
1716512823916_Sectors_of_Indian_Economy_X.pptx
kavishtiwari2009
 
Air India Plane Crash: Tragedy Strikes Near Ahmedabad
Air India Plane Crash: Tragedy Strikes Near Ahmedabad
New India Abroad
 
Οι χώρες της Ευρώπης με την καλύτερη ποιότητα υδάτων για κολύμβηση – Η θέση τ...
Οι χώρες της Ευρώπης με την καλύτερη ποιότητα υδάτων για κολύμβηση – Η θέση τ...
Newsroom8
 
Arab Spring. protect against corrupt leaders of arab
Arab Spring. protect against corrupt leaders of arab
ShukrullahBaloch
 
INDIAN VACCINE DIPLOMACY: VACCINE MAITRI
INDIAN VACCINE DIPLOMACY: VACCINE MAITRI
DrKAPILKUMARGOEL
 
Kerala a hotspot for ISIS recruitment.pdf
Kerala a hotspot for ISIS recruitment.pdf
Aaryan Kansari
 
the pay postcode lottery event slides.pdf
the pay postcode lottery event slides.pdf
ResolutionFoundation
 
TCS to set up IT facility housing 10k employees in Vizag: Nara Lokesh
TCS to set up IT facility housing 10k employees in Vizag: Nara Lokesh
narsireddynannuri1
 
UPSC Officers News & Insights: Who’s Moving Where in Indian Administration.pdf
UPSC Officers News & Insights: Who’s Moving Where in Indian Administration.pdf
harshitbaisla73
 
Gatwick AG About A Company Shut Down by UK Courts Still Sells Offshore Servic...
Gatwick AG About A Company Shut Down by UK Courts Still Sells Offshore Servic...
nathaniel leonard
 
Connecting the Indian Community Abroad – New India Abroad
Connecting the Indian Community Abroad – New India Abroad
New India Abroad
 
Vijay Rupani Biography: Early Life, Education, and Political Career
Vijay Rupani Biography: Early Life, Education, and Political Career
VoterMood
 
Indian Community Centers – New India Abroad
Indian Community Centers – New India Abroad
New India Abroad
 
POLITICAL AND RELIGIOUS ASPECTS OF POWER IN INDIA
POLITICAL AND RELIGIOUS ASPECTS OF POWER IN INDIA
Anushka Singh
 

Avoiding Callback Hell with Async.js

Editor's Notes

  • #2: Welcome. I’m going to talk today about callback hell, a place well-traveled by experienced javascript developers, as well as some other common control flows and patterns that make javascript – less than comfortable sometimes. I’m going to focus on Async.js, a useful JavaScript library for managing complex control flows. Async also has a number of other handy tricks up its sleeve to make your life easier, and I’ll show you some of them.
  • #3: So hi. I’m Aaron. Here’s a few places you can contact me, should you want to ask any questions later, or troll my posts.
  • #4: So, JavaScript? You guys know it, right? A language we all know and love. Once relegated to small-scale browser scripting, but now exploding into use everywhere. Remember back when Java was supposed to be in everything from servers to toasters? I think javascript has taken up that mantle.
  • #5: So why do I like javascript so much? Well, it’s a really nice language. C-ish syntax, efficiency and flexibility in the object model. And once people started really working with javascript, they discovered that it actually has an excellent event model, which gave it some robust asynchronous programming capabilities. Now, asynchronous-focused programing languages are nothing new, as anyone who’s worked with actionscript can attest, but Javascript has a particularly nice model. Also, javascript is now both a client and server-side language, thank to…
  • #6: …node.js. In fact, the pre-existing event model was one of the reasons javascript was such a perfect fit for an asynchronous server-side framework like Node.
  • #7: So javascript’s great, we all know it, but let’s talk about the skeletons in the closet. Every language has things that can cause you pain, and javascript is no exception.
  • #8: Javascript handles its asynchronous control flow using callbacks. Callbacks, while useful, allow your code to jump to any other spot in the code instantly, upon completion of a function. Because of this, some have described callbacks at the modern-day GOTO statement – and hopefully we all remember why GOTO is evil. Impossible to trace code, difficult to read and understand – its a maintainability issue. This is actually a really big deal, because low readability and maintainability is what inhibits certain languages, frameworks, and syntax paradigms from enabling the creation of large, long-lasting code bases. We are starting to build huge, full stack stuff with javascript, which means we have to start being very cognizant of the fact that large potentially numbers of developers with varying levels of expertise and understanding will need to look at our code, modify and extend our code, and debug our code.
  • #9: So let’s take a look at a callback. Point of note, most of my examples are going to be written in node.js syntax, because I’m a node guy and its easier to demo, but async.js itself, though originally written for node.js, now works in both client and server side javascript. So here’s a standard callback. We call the readfile method, pass it some parameters (a file and a format), and then pass it a callback function.
  • #10: Standard javascript convention is to use inline, anonymous functions as callbacks, which is what we see here.
  • #11: Because this conventional syntax often causes some confusion, let’s pick it apart to make sure its completely clear. Here’s equivalent, if slightly less elegant, syntax, separating out the entire anonymous function declaration. So there it is.
  • #12: The problem comes in when you start nesting callbacks. As in, my callback from an asyncronous function calls another asynchronous funciton, which has its own callback. And on, and on…
  • #13: We in the biz’ call this callback hell. Because its scary. And painful.
  • #14: And has a devil.
  • #15: Now let’s visualize what’s going on here. When a function is called, it goes off into the asynchronous, background ether. And the rest of the file continues to execute. When the function finishes its asynchronous task, it calls the callback. This ends up looking something like this, except the functions aren’t calling each other directly. Regardless, in the best case scenario, we have a linear callback path. Function 1 finishes, calls function 2. function 2 finishes, calls function 3. etc. Now without the asynchronous component, we’d be all set. These functions would call each other in a cascading fashion. But alas, these callbacks are asynchronous.
  • #16: Which means they can _branch_
  • #17: If I loop over a collection in function 1, and call an asynchronous function for each of 2 elements, I get 2 calls to its callback, function 2. If in one instance of func 2, we iterate over another collection, we get three instances of function 3. And each of these branches instances calls the full chain of future callbacks. Starting to see how this can get really out of hand?
  • #18: So other than being able to draw a non-confusing diagram, what’s the problem with branching? First, we lose any control over the order of calls within our callback chain. It’s unlikely, but theoretically a single branch could play out all the way down before any other branch starts. We are getting parallel execution, for some definition of parallel, but each branch will end with its own piece of data as a result of the full execution chain. Since we don’t know the order of execution, its tricky to aggregate all of these final return data values without global variables. Further, understanding and managing scope within so many nested functions can become extremely difficult.
  • #19: Here’s a more realistic piece of pseudocode. This is a very simple piece of code. It makes an http request, gets a response containing a number of recent posts (let’s assume this query is going to a blogging app with an API), opens a connection to a database, extracts the id of the user who wrote the post, and queries information about that user from the database. Even in this basic functionality, we have accumulated three nested callbacks, three anonymous inline functions.
  • #20: Here’s our friend. We can already see the nesting begin, without too much complexity to the functionality of our code. Whathappens when we add error handling? More nesting, harder to read code. Code that’s becoming harder to maintain.
  • #21: The first step in improving understandability and readability is to separate out those anonymous functions. Instead of passing an entire anonymous function into our asynchronous method, I’d rather pass a reference to a function formally defined elsewhere.
  • #22: So let’s remove the inline callback…
  • #23: … and define it as an object. We can then pass it into the async method. This is much easier to read, which is good.
  • #25: Sure, a few more lines of code. But look how much better the organization is. Look how much easier it would be to come in and immediately understand this code.
  • #27: So let’s talk about a solution to our control flow problems. Async.js is a fantastic library created by Caolan McMahon. Async gives you easy to use tools for a number of common control flows to address the problems we’ve been talking about, and also gives you some advanced collection management tools in the form of common functional programming paradigms. Let’s take a look so you can see what I mean.
  • #29: The first set of tools async provides are control flows for serial and parallel execution of functions. In some cases, you will want to run a number of asynchronous functions in series (while still allowing other code to execute during async operations). In many other cases, you want to run all of your functions in parallel, but to aggregate results when all computations are complete, as we discussed earlier. Async gives you nice, lean syntax to implement both of these control flows:
  • #30: Like so. Just pass a list of functions into an async.parallel or async.series function, and async will execute them as you have requested. You can declare these functions as inline anonymous functions, or separate them into independent function objects as I showed you earlier, to make the control flow code very small and readable. But wait, there’s more!
  • #31: You don’t have to manage the callback data pipeline directly anymore! For parallel execution, I can pass in as many functions as I want, and a single callback for all of them. Async will aggregate all return values from each function together into a single collection, and call the universal control flow callback when all parallel executions have completed. All of your data, in one place, delivered to you when computation is complete, with no work on your part. If you have ever had to deal with a lot of parallel execution, this is seriously cool. Refactoringcontrol flows is now much easier as well – you can see the small change to go from series to parallel. Just change the async method, and remove or add a callback. Two lines of code to change your entire execution strategy. This can be very powerful when you have complex systems and interactions.
  • #32: The one drawback to serial execution is that while async executes each function, one after the other, the functions have no way of talking to each other.