node.js workshop- javascript async

29
node.js workshop Hochschule Darmstadt, May 2014 presented by Microsoft Student Partners in cooperation with gfnork

Upload: qiong-wu

Post on 06-May-2015

296 views

Category:

Software


3 download

TRANSCRIPT

Page 1: node.js workshop- JavaScript Async

node.js workshopHochschule Darmstadt, May 2014

presented by Microsoft Student Partners

in cooperation with gfnork

Page 2: node.js workshop- JavaScript Async

What is Synchronicity?

Page 3: node.js workshop- JavaScript Async

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.

Page 4: node.js workshop- JavaScript Async

Visualisation of synchronicity• Simple example: reading a book!• Synchronous…

Open chapter Loading Read chapter Next chapter Loading Read chapter

Page 5: node.js workshop- JavaScript Async

Visualisation of synchronicity• In contrast: reading a book asynchronous!

Loading #1

Chapter 1

Loading #2

Chapter 2

Loading #3

Chapter 3

Page 6: node.js workshop- JavaScript Async

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!

Page 7: node.js workshop- JavaScript Async

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.

Page 8: node.js workshop- JavaScript Async

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.

Page 9: node.js workshop- JavaScript Async

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.

Page 10: node.js workshop- JavaScript Async

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.

Page 11: node.js workshop- JavaScript Async

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.

Page 12: node.js workshop- JavaScript Async

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!”

Page 13: node.js workshop- JavaScript Async

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

Page 14: node.js workshop- JavaScript Async

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!

Page 15: node.js workshop- JavaScript Async

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!

Page 16: node.js workshop- JavaScript Async

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.

Page 17: node.js workshop- JavaScript Async

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!

Page 18: node.js workshop- JavaScript Async

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.

Page 19: node.js workshop- JavaScript Async

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.

Page 20: node.js workshop- JavaScript Async

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');

Page 21: node.js workshop- JavaScript Async

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.

}

}

}

}

}

Page 22: node.js workshop- JavaScript Async

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!

Page 23: node.js workshop- JavaScript Async

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!

Page 24: node.js workshop- JavaScript Async

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]

Page 25: node.js workshop- JavaScript Async

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”.

Page 26: node.js workshop- JavaScript Async

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“

Page 27: node.js workshop- JavaScript Async

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]

Page 28: node.js workshop- JavaScript Async

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.