node.js workshop- JavaScript Async
Transcript of node.js workshop- JavaScript Async
node.js workshopHochschule Darmstadt, May 2014
presented by Microsoft Student Partners
in cooperation with gfnork
What is Synchronicity?
Is Javasript asynchron?• No!
• Javascript is synchron and even worse single-threaded. To be correct, JS is interpreted synchronous and the interpreters run single-threaded.• Single-threaded means only one command at a times is processed.
So you might ask, why the hell does anyone want to code asynchronous in Javascript?
• The answer is very simple: Because we want to.• This is because we want to click a button on our page and have it load
some other stuff, while the rest of the page is still accessible. We want to show already loaded parts of the gallery and don’t want to wait until everything is downloaded and then start showing off all content.
Visualisation of synchronicity• Simple example: reading a book!• Synchronous…
Open chapter Loading Read chapter Next chapter Loading Read chapter
Visualisation of synchronicity• In contrast: reading a book asynchronous!
Loading #1
Chapter 1
Loading #2
Chapter 2
Loading #3
Chapter 3
How can JS be asynchronous?• Well even though JS is synchronous single-threaded, we
can start asynchronous tasks with one command at a time. Imagine a king, who sends out his knights and servants to do certain things.• The king has no idea what one knight is doing at the moment.
But he has given instructions. And the knight has to come back and give a report to the king. This makes it possible for him to do many things parallel through his servants. • While a servant is on his way doing something, the king
cannot contact him. He cannot stop him from accomplishing his orders. And furthermore he doesn’t know if his knights get killed on the mission or desert. Remember: Back in these days mobile phones did not exist!
Knights and Kings• You might say we live in the age of mobile communication and
the knights should have mobile phones with them. The answer is yes, there are some parallel programming paradigms which can do that, for example Open MPI. In this country the king has a leash and can always control his servants.
• In computers this is called threads with process IDs. You can send messages to threads and control them, kill them, spawn more and have them waiting for work and so on. But this is the country of Java and C++. In the mighty realm of Javascript we don’t want to control our Knights tightly, because everything should run smooth and fast.
Basic examples• Before we get totally lost in the middle ages, let’s have
an example:
var fs = require('fs');
var content = fs.readFileSync('index.html');
console.log(contents);Well, the content is loaded and then displayed in the console. But while it is being loaded, nothing else will happen. If our hard disk is much slower than our CPU, we could do other things in between. And no surprise,
this is near to always the case.
Async File Read• Then let’s have an alternative example:
var fs = require('fs');
fs.readFile('index.html', function(err, contents){
console.log(contents);
});
var fs = require('fs'); fs.readFile('index.html', function(err, contents){ console.log(contents); });
Remember anonymous functions? Here is one, let‘s
name it a callback.
Callbacksvar fs = require('fs');
var callback = function(err, contents){ console.log(contents); }
fs.readFile('index.html', callback);
• We could have done it this way as well. The function acts the same but it is no longer anonymous.
• So what are callbacks? Well, this is the order the knights get from the king, what they have to do after finishing or how to report back at a certain stage.
Async File Readvar http = require('http');
var fs = require('fs');
http.createServer(function(request, response) {
response.writeHead(200);
fs.readFile('index.html', function(err, contents) {
response.write(contents);
response.end();
});
}).listen(8080);
Callback #1
Callback #2
This might look magical at first glance. Keep in mind that functions are objects as
well. Here an object is created which handles a request and a response. The first
callback is called when createServer is finished and the second after the readFile is
finished.
In royal terms• The king says: “My loyal first knight, go forth and create server!”
http.createServer(…)
• He continues: “Afterwards, write head and read the file index.html!”
function(request, response) {
response.writeHead(200);
fs.readFile('index.html',…)
• The knight accepts this order, but he thinks “file read is no fun, let my page do that for me!”
function(err, contents) { response.write(contents); response.end(); });
• Furthermore he says his page: “After you have done that, put the content into this response letter and I will bring it to the king!”
async.eachvar async = require('async');
var gather_troops = function(num, Callback) {
console.log('squad '+num+' reporting in!');
return Callback(null);
};
async.each([1, 2, 3, 4], gather_troops, function (err) {
console.log("Army ready!");
});
The package async is required
The king wants to gather troops!
The king gives out orders through
async.each
Building an asynchronous army!async.each([1, 2, 3, 4], gather_troops, function (err) { console.log("Army ready!"); });
• The function async.each will run the function gather_troops with each entry in the given array [1,2,3,4] and afterwards write a report to the console.
squad 1 reporting in!squad 2 reporting in!squad 3 reporting in!squad 4 reporting in!
Army ready!
async.mapvar make_delicious_course = function (num, Callback) {
return Callback(null, num+'. course');
};
async.map([1, 2, 3, 4], make_delicious_course, function (err, results) {
console.log(results);
console.log("Lordes, disheth ready!");
});
map instead of each! The callback carries a string now!
The results are printed here and not
in the called function!
map vs each• Why is the king very careful that in the last example
map is used instead of each?• Because each does not return an array. When the army is
gathered, he only wants to hear from them, that they are ready. But when food is cooked, returning the courses is very important.• Map does return an array. Therefore the king can eat the food,
because he has the food at hand.
• So if just want to execute some things parallel, just use each. But if you need something back, map will be the better option. Speaking in websites, in one case you want to display some <divs> in another case you need reference to some spawned buttons, too.
Races• T
o be honest, the cooks need different amount of time for preparing the courses.
var make_delicious_course = function (num, Callback) {
setTimeout(function() {
console.log("dish #"+num+" is being prepared!");
return Callback(null, num+'. course');
}, 100 * Math.random());
};• W
hat does that mean? Well we have just added a timeout that waits until some time is over and then proceeds. Thus simulating different amounts of time the cooks will need.
setTimeout takes an anonymous callback
function as argument!
Races• Ok, let’s try it out!
async.map([1, 2, 3, 4], make_delicious_course, function (err, results) { console.log(results); console.log("Lordes, disheth ready!");
}); dish #2 is being prepared!dish #1 is being prepared!dish #3 is being prepared!dish #4 is being prepared![ '1. course', '2. course', '3.
course', '4. course' ]Lordes, disheth ready!
This may be surprising. We see that our asynchronous tasks run in an arbitrary order. That is not too surprising, since we understand what asynchrony means. But still the array is in the right order. This is because the async.js library takes care of the order in this case. If we had build a function “map” by ourselves, we would have to remember the running order and sort the array in the end.
Races• Races occur everywhere in asynchronous code. But if
code is designed carefully they are never a problem at all.
• The whole design how asynchronous programming is done in Javascript prevents critical implications of races very well. This is because you don’t know what your knights are doing. And they don’t know what other knights are doing. Therefore critical data races occur especially when handling global variables.
• But we all know global variables are bad style. In parallel programming this is even more the case.
• Debugging races can be painful – even for kings.
Convention• Let’s take a look at a callback function:
function (err, results) {
console.log(results);
console.log("Lordes, disheth ready!");
}• Maybe you have noticed that all take arguments in the order “error” and then “something” to proceed with. In this case these arguments were “err” and “results”.
There is a convention in the community of JS to put error handling first and the rest afterwards. Most libraries stick to this behavior and it is advisable to stick to it as well. If you don’t want to handle errors, because you are the king or so, then you can still pass “null”:
return Callback(null, num+'. course');
Christmas Trees• C
hristmas trees are nice, because there are a lot of surprises below. In Javascript it is the same, that is why Josh Wright has coined this name in his tutorial. How does a Christmas tree look like?
asyncFunc1(function(err, result) {
asyncFunc2(function(err, result) {
asyncFunc3(function(err, result) {
asyncFunc4(function(err, result) {
asyncFunc5(function(err, result) {
// It's Christmas! Praise the lord etc.
}
}
}
}
}
Christmas Trees• So basically this just means that each async call leads
to the source code being indented and you lose overview really fast.
• Others name this effect “callback hell”
• Furthermore we haven’t seen error handling yet. This adds in to the topic “our code gets swollen up”
• In the final part of this introduction to asynchronous programming in Javascript, we will see at least one way to trim code and make it look more like synchronous code.
• So let’s have a look at promises!
Registration – classic callbacks
this.register = function (email, callback) {
_save(email, function (err) {
if (err)
return callback(err);
_send(email, function (err) {
callback(err);
});
});
};
};
var Registration = function () {
var _save = function (email, callback) {
setTimeout(function(){
callback(null);
}, 20);
};
var _send = function (email, callback) {
setTimeout(function(){
callback(new Error("Failed to send"));
}, 20);
};
Yay! Christmas Tree!
Classic callbacks in action• We have a class “Registration”,
which allows us to register, send and save users. All functions are just dummies to show how error handling works. We use it with the following code:
var registry = new Registration();
registry.register("[email protected]", function (err) {
console.log("done", err);
});
• The output tells us that the built-in error occurred and was printed successfully. Now let’s do the same with promises…
done [Error: Failed to send]
Promises• Also called Futures. But for node.js promise is the
common name.
• A promise can have different states:• fulfilled• rejected• pending• (settled)
• If your function is used as a promise, you can say what happens when the promise has fulfilled or rejected. Or more precise, what happens “then”.
Promisesvar deferred = require('deferred');
var Registration = function () {
var _save = deferred.promisify(function (email, callback) { callback(null); });
var _send = deferred.promisify(function (email, callback) { callback(new Error("Failed to send"));
});
this.register = function (email, callback) {
//chain two promises together and return a promise
return _save(email).then(_send);
};
};
The function is promisified
Therefore it becomes „thenable“
Promises in actionvar registry = new Registration();
registry.register("[email protected]")
.then(
function () {
console.log("Success!");
},
function (err) {
console.log("Failed with error:", err);
}
);
Failed with error [Error: Failed to send]
Promises• Promises let your asynchronous code look more usual.
No Christmas tree effect, no callback hell.
• Furthermore you can collect all errors in nested functions, which call each other with then. Then you can throw all errors at once.
• Promises standardize the way asynchronous code is managed. Functions you run have a precise state of either pending or settled. When settled you know if they fulfilled or failed (rejected).
• Promises are not a must have, but they give the feeling of returning code back.
Further read and sources• http://javascriptplayground.com/blog/2013/06/think-asy
nc/
• http://www.joshwright.com/tips/javascript-christmas-trees-promises-and-event-emitters
• http://www.html5rocks.com/en/tutorials/es6/promises/