Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser. Upcoming version 10 of Internet Explorer should also handle it.

async.js

Does this look familiar?
var fs = require('fs'),
  path = require('path'),
  file1 = "file1.txt",
  file2 = "file2.txt";

// Check if file1 exists, write to file2,
// then display new file2 contents.
path.exists(file1, function (exists) {
  if (!exists) throw new Error("No file!");

  fs.readFile(file1, function (err, data) {
    if (err) throw err;

    fs.writeFile(file2, data, function (err) {
      if (err) throw err;

      fs.readFile(file2, function (err, data) {
        if (err) throw err;
        console.log(data.toString());
      });
    });
  });
});
fun1(params, function (err, results) {
  fun2(params, function (err, results) {
    fun3(params, function (err, results) {
      fun4(params, function (err, results) {
        fun5(params, function (err, results) {
          fun6(params, function (err, results) {
            fun7(params, function (err, results) {
              fun8(params, function (err, results) {
                fun9(params, function (err, results) {
                  fun10(params, function (err, results) {
                    // Do something interesting here.
                  });
                });
              });
            });
          });
        });
      });
    });
  });
});

Isn't this a bit frustrating and complicated?

async.js

A control-flow library for
Node.js and the browser.


github.com/caolan/async

Serial Control Flow

  • series
  • iterator
  • waterfall
var readData; // Chained data object.

// Straight series.
async.series([
  function (cb) {
    path.exists(file1, function (exists) {
      cb(exists ? null : new Error("No file!"));
    });
  },
  function (cb) {
    fs.readFile(file1, function (err, data) {
      readData = data;
      cb(err);
    });
  },
  function (cb) {
    fs.writeFile(file2, readData, cb);
  },
  function (cb) {
    fs.readFile(file2, function (err, data) {
      if (!err) console.log(data.toString());
      cb(err);
    });
  }
], function (err) {
  if (err) throw err;
});

Pitfall: Out-of-order execution
and chained objects.

// Now with waterfall...
async.waterfall([
  function (cb) {
    path.exists(file1, function (exists) {
      cb(exists ? null : new Error("No file!"));
    });
  },
  function (cb) {
    fs.readFile(file1, cb); // Pass cb directly.
  },
  function (data, cb) { // "data" now first arg.
    fs.writeFile(file2, data, cb);
  },
  function (cb) {
    fs.readFile(file2, function (err, data) {
      if (!err) console.log(data.toString());
      cb(err);
    });
  }
], function (err) {
  if (err) throw err;
});

Parallel Control Flow

  • parallel
  • queue
// Parallel.
async.parallel({
  read1: function (cb) {
    fs.readFile(file1, cb);
  },

  read2: function (cb) {
    fs.readFile(file2, cb);
  }
}, function (err, results) {
  if (err) throw err;

  // Our results object with named members.
  console.log(results.read1.toString() +
              results.read2.toString());
});

Collections

  • forEach | forEachSeries
  • map | mapSeries
  • filter | filterSeries
  • reduce | reduceRight
  • concat | concatSeries
function readPrint(file, callback) {
  fs.readFile(file, function (err, data) {
    console.log(data.toString());
    callback(err, file);
  });
}

// Collection "forEach" iteration.
async.forEach([file1, file2], readPrint, function (err) {
  if (err) throw err;
});

// Mapped collection (also aggregates returned result).
async.map([file1, file2], readPrint, function (err, results) {
  if (err) throw err;
  console.log("File names: " + results);
});

Everything Together

  • auto: dependency graphs!
// Read two files and write to third.
async.auto({
  read1:  function (cb) {
    fs.readFile(file1, cb);
  },

  read2:  function (cb) {
    fs.readFile(file2, cb);
  },

  write3: ['read1', 'read2', function (cb, results) {
    var data = results.read1.toString() +
               results.read2.toString();
    fs.writeFile(file3, data, cb);
  }],

  read3:  ['write3', function (cb) {
    fs.readFile(file3, cb);
  }]
}, function (err, results) {
  if (err) throw err;
  console.log(results.read3.toString());
});

Thanks!

slides - bit.ly/nodedc-async
async.js - github.com/caolan/async

meetup.com/Nova-Node debuting 3/20/2012!
SpanishDict.com is hiring!
ryan@loose-bits.com

Use a spacebar or arrow keys to navigate