Put on Your Asynchronous Hat and Node

39
#wdc13 Put On Your Asynchronous Hat and Node Marc Fasel @marcfasel Shine Technologies http://blog.shinetech.com 1 Saturday, 4 May 13

description

Introduction in asynchronous programming in Node.js, async.js, and q promises.

Transcript of Put on Your Asynchronous Hat and Node

Page 1: Put on Your Asynchronous Hat and Node

#wdc13

Put On Your Asynchronous Hat and Node

Marc Fasel @marcfasel

Shine Technologies http://blog.shinetech.com

1Saturday, 4 May 13

Page 2: Put on Your Asynchronous Hat and Node

#wdc13

What is an Asynchronous Hat?

2Saturday, 4 May 13

Page 3: Put on Your Asynchronous Hat and Node

#wdc13

Node.jsServer-side JavaScript platform

Event-driven, non-blocking I/O

All I/O is asynchronous

Asynchrononous everywhere

3Saturday, 4 May 13

Page 4: Put on Your Asynchronous Hat and Node

#wdc13

Different MindsetsSynchronous coding comes natural

Natural execution order

Asynchronous coding a bit harderSerial execution leads to nesting

New: parallel execution

Single Callback functions and Event Emitters

Explicit error handling

4Saturday, 4 May 13

Page 5: Put on Your Asynchronous Hat and Node

#wdc13

Synchronous Code

var filenames = fs.readdirSync('/tmp/');for (var i = 0; i < filenames.length; i++) { console.log(filenames[i]);}console.log('Done.');

5Saturday, 4 May 13

Page 6: Put on Your Asynchronous Hat and Node

#wdc13

Asynchronous Code

fs.readdir('/tmp/', function (err, filenames) { for (var i = 0; i < filenames.length; i++) { console.log(filenames[i]); } console.log('Done.');});

6Saturday, 4 May 13

Page 7: Put on Your Asynchronous Hat and Node

#wdc13

Some More Synchronous Codevar totalBytes = 0;

for (var i = 0; i < filenames.length; i ++) { var stats = fs.statSync('/tmp/' + filenames[i]); totalBytes += stats.size;}

console.log('Total bytes: ' + totalBytes);

7Saturday, 4 May 13

Page 8: Put on Your Asynchronous Hat and Node

#wdc13

Asynchronous Parallel Execution

var count = filenames.length;for (var i = 0; i < filenames.length; i++) {! fs.stat('/tmp/' + filenames[i], function (err, stats) {! ! totalBytes += stats.size;! ! count--;! ! if (count === 0) {! ! ! // We’re done! ! ! console.log('Total bytes: ' + totalBytes);! ! }! });};

8Saturday, 4 May 13

Page 9: Put on Your Asynchronous Hat and Node

#wdc13

fs.readFile(dirFrom+filename,'utf-8', function (err, data){! ! if (err) return callback(err);! ! fs.writeFile(dirTo+filename,data,'utf-8', function (err){! ! ! ! if (err) return callback(err);! ! ! ! fs.chmod(dirTo+filename,0777, function (err){! ! ! ! ! ! if (err) return callback(err);! ! ! ! ! ! anotherAsyncFunction(param1, function (err){! ! ! ! ! ! ! ! if (err) return callback(err);! ! ! ! ! ! ! ! anotherAsyncFunction(param1, function (err){! ! ! ! ! ! ! ! })! ! ! ! ! ! })! ! ! ! })! ! })});

Pyramid of Doom

9Saturday, 4 May 13

Page 10: Put on Your Asynchronous Hat and Node

#wdc13

Event Mechanisms

Events used for asynchronous communication

Two types of asynchronous functions:Single Callback:  one ‘done’ event, single callback function

Event Emitter:  more than one event

10Saturday, 4 May 13

Page 11: Put on Your Asynchronous Hat and Node

#wdc13

Single Callback Functions

fs.readdir(dir, function (err, filenames) {// Failure

! if (err) return callback(err);

// Success! console.log(dir + ' has ' + filenames.length + 'files.');});!

11Saturday, 4 May 13

Page 12: Put on Your Asynchronous Hat and Node

#wdc13

Event Emitter

var readStream = fs.createReadStream(filename);

readStream.on('open', function () {! readStream.pipe(res);});

readStream.on('error', function(err) {! // Failure! res.end(err);});

12Saturday, 4 May 13

Page 13: Put on Your Asynchronous Hat and Node

#wdc13

Error HandlingSynchronous

Exceptions give us lots of goodies

Asynchronous Explicit error code everywhere

Lots of boilerplate

More Pyramid of Doom

13Saturday, 4 May 13

Page 14: Put on Your Asynchronous Hat and Node

#wdc13

Synchronous Error Handling

We are blessed with Exceptions

Stop execution immediately

Automatically bubble up call hierarchy

Error code logically separated from application code

14Saturday, 4 May 13

Page 15: Put on Your Asynchronous Hat and Node

#wdc13

World With Exceptionsfunction readFile(fileName) {! var file = openFile(fileName);! var data = readContent(file);! closeFile(file);! return data;}

function readContent(file) {! throw new Error("Exception!");}

try {! var myData = readFile(fileName);} catch (e) {! // Handle error!}

15Saturday, 4 May 13

Page 16: Put on Your Asynchronous Hat and Node

#wdc13

World Before Exceptionsfunction readFile(err, filePath) {! var file = openFile(err, filePath);! if (err) { return err; }

! var data = readContent(err, file);! if (err) { return err;}

! closeFile(err, file);! if (err) { return err; }

! return data;}

var myFileContent = readFile(err,filePath);if (err) { // Handle error };

16Saturday, 4 May 13

Page 17: Put on Your Asynchronous Hat and Node

#wdc13

Asynchronous Error Handlingtry { fs.readFile('/tmp/test.txt', 'utf-8', function (err, data) { !if (err) { !! throw err; !} console.log(data); });!} catch (e) {! console.log(e);}

Exceptions possible

try/catch does not have desired result

17Saturday, 4 May 13

Page 18: Put on Your Asynchronous Hat and Node

#wdc13

Callback Error Handling

fs.readFile('/tmp/test.txt', 'utf-8', function (err, data) {! if (err) return callback(err);

! // Success! console.log(data);});!

18Saturday, 4 May 13

Page 19: Put on Your Asynchronous Hat and Node

#wdc13

Event Emitter Error Handling

var readStream = fs.createReadStream(filename);readStream.on('open', function () {! readStream.pipe(res);});readStream.on('error', function(err) { res.end(err);});

19Saturday, 4 May 13

Page 20: Put on Your Asynchronous Hat and Node

#wdc13

Pyramid of Doom With Error Handlingfs.readFile(dirFrom+filename,'utf-8', function (err, data){! ! if (err) return callback(err);! ! fs.writeFile(dirTo+filename,data,'utf-8', function (err){! ! ! ! if (err) return callback(err);! ! ! ! fs.chmod(dirTo+filename,0777, function (err){! ! ! ! ! ! if (err) return callback(err);! ! ! ! ! ! anotherAsyncFunction(param1, function (err){! ! ! ! ! ! ! ! if (err) return callback(err);! ! ! ! ! ! ! ! anotherAsyncFunction(param1, function (err){! ! ! ! ! ! ! ! ! if (err) return callback(err);! ! ! ! ! ! ! ! ! callback();! ! ! ! ! ! ! ! })! ! ! ! ! ! })! ! ! ! })! ! })});

20Saturday, 4 May 13

Page 21: Put on Your Asynchronous Hat and Node

#wdc13

Asynchronous Error Handling

Can’t catch exceptions that happen in callbacks

Explicit code to handle error

Simple Callback: error code gets mixed with application code

Event Emitter: error code separate

21Saturday, 4 May 13

Page 22: Put on Your Asynchronous Hat and Node

#wdc13

Help is Available

22Saturday, 4 May 13

Page 23: Put on Your Asynchronous Hat and Node

#wdc13

Named Callbacksfs.readdir('/tmp/', readDirCallback);

function readDirCallback (err, filenames) { for (var i = 0; i < filenames.length; i++) { console.log(filenames[i]); } console.log('Done.');};

23Saturday, 4 May 13

Page 24: Put on Your Asynchronous Hat and Node

#wdc13

Nesting Named Callbacks

function copyFile(filePathFrom, filePathTo, callback){! fs.readFile(filePathFrom,'utf-8', readFileCallback);

! function readFileCallback(err, data){! ! if (err) return callback(err);! ! fs.writeFile(filePathTo, data, 'utf-8', writeFileCallback);

! ! function writeFileCallback(err){! ! ! if (err) return callback(err);! ! ! callback();! ! };! };};

24Saturday, 4 May 13

Page 25: Put on Your Asynchronous Hat and Node

#wdc13

Control Flow libraries

Fix problems with asynchronous Control Flow

Control flow first thing people want to fix

Many control flow libraries available

25Saturday, 4 May 13

Page 26: Put on Your Asynchronous Hat and Node

#wdc13

Async.js

The Underscore.js of asynchronous code

Control flow constructs

Functional constructs

26Saturday, 4 May 13

Page 27: Put on Your Asynchronous Hat and Node

#wdc13

Seriesasync.series([! function(callback) {! ! fs.chmod(file1,0777, function (err){! ! ! if (err) return callback(err);! ! ! callback ();! ! });! },! function(callback) {! ! fs.chmod(file2,0777, function (err){! ! ! if (err) return callback(err);! ! ! callback();! ! });! }],! function done(err) {! ! if (err) return console.log(err);! ! console.log ('Done.');! });

27Saturday, 4 May 13

Page 28: Put on Your Asynchronous Hat and Node

#wdc13

Parallelasync.parallel([! function(callback) {! ! fs.chmod(file1,0777, function (err){! ! ! if (err) return callback(err);! ! ! callback ();! ! });! },! function(callback) {! ! fs.chmod(file2,0777, function (err){! ! ! if (err) return callback(err);! ! ! callback();! ! });! }],! function done(err) {! ! if (err) return console.log(err);! ! console.log ('Done.');! });

28Saturday, 4 May 13

Page 29: Put on Your Asynchronous Hat and Node

#wdc13

Functional Constructs

async.map(['file1','file2','file3'], fs.stat, function(err, results){ // results is now an array of stats for each file});

async.filter(['file1','file2','file3'], path.exists, function(results){ // results now equals an array of the existing files});

async.reduce(...);

29Saturday, 4 May 13

Page 30: Put on Your Asynchronous Hat and Node

#wdc13

Promises

Pattern for asynchronous control flow

Promises are objects representing a future outcome of an asynchronous call

Outcome will be either ‘resolved’ or ‘rejected’

Attach callbacks to ‘resolved’, ‘rejected’

30Saturday, 4 May 13

Page 31: Put on Your Asynchronous Hat and Node

#wdc13

Callback -> Promise

function getReadFilePromise(){! var deferred = q.defer();! fs.readFile('/tmp/test.txt', 'utf-8', function (err, data) {! ! if (err) { deferred.reject(err); }! ! deferred.resolve(data);! });! !! return deferred.promise;}

31Saturday, 4 May 13

Page 32: Put on Your Asynchronous Hat and Node

#wdc13

Promises

var readFilePromise = getReadFilePromise();

readFilePromise.then( function(data){ console.log('Resolved:' + data); }, function(err){ console.log('Rejected:' + err); });

32Saturday, 4 May 13

Page 33: Put on Your Asynchronous Hat and Node

#wdc13

Promises

Cool Stuff: Promise Chaining

33Saturday, 4 May 13

Page 34: Put on Your Asynchronous Hat and Node

#wdc13

Promise ChainingreadFilePromise(filePathFrom)! .then(writeFilePromise(filePathTo))! .then(changeFilePermissionPromise(filePathTo,'777'))! .then(oneMorePromise())! .then(oneMorePromise());

var promises = [! readFilePromise,! writeFilePromise,! changeFilePermissionPromise,! oneMorePromise,! oneMorePromise];

var allPromise = Q.all(promises);

34Saturday, 4 May 13

Page 35: Put on Your Asynchronous Hat and Node

#wdc13

Promise Error Handling

readFilePromise(filePathFrom)! .then(writeFilePromise(filePathTo))! .then(changeFilePermissionPromise(filePathTo,'777'),! ! errorCallback);

35Saturday, 4 May 13

Page 36: Put on Your Asynchronous Hat and Node

#wdc13

Promises

Promises help with asynchronous control flow

Avoid the Pyramid of Doom

Exception style error bubbling

36Saturday, 4 May 13

Page 37: Put on Your Asynchronous Hat and Node

#wdc13

ConclusionAsynchronous programming needs an asynchronous hat

New thingsCallbacks

Event Emitters

Explicit error handling

For the more difficult stuffNamed callbacks

Async.js

Promises37Saturday, 4 May 13

Page 38: Put on Your Asynchronous Hat and Node

#wdc13

Thank you.

38Saturday, 4 May 13

Page 39: Put on Your Asynchronous Hat and Node

#wdc13

ReferencesTrevor Burnham - Async JavaScript: Build More Responsive Apps with Less Code

Pedro Teixeira - Professional Node.js: Building Javascript-Based Scalable Software

You’re Missing the Point of Promises - http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/

Popular Control Flow Libraries - http://dailyjs.com/2011/11/14/popular-control-flow/

39Saturday, 4 May 13