Functional Asynchronous Programming in node.js

Using callbacks is the node.js way to return the value of the asynchronous operations[1]. Callback is a function receiving two arguments: error and result.

// callback for the file reading operation
var print = (err, data) => console.log(data);

require('fs').readFile('text.txt', {encoding:'utf8'}, print);

The callback function type is error -> mixed -> (), where error is the error type and mixed denotes value of any JS type (object, string, number, etc).

Let's create the function value that converts the given value to function with one callback argument:

var value = v => cb => cb(null, v);

Its typedef is mixed -> ((error -> mixed -> ()) -> ()). The function receives some value and returns function that receives callback and immediately runs it, providing the given value as its result.

This function makes calls to asynchronous functions replaceable with values without affecting the control flow.

value('ok', print);

Now construct the read and write functions that receive and return values:

var read = filepath =>
  callback =>
    filepath((_, filepath) => 
        { encoding : 'utf8' },

var write = (filepath, data) =>
  callback =>
    filepath((_, filepath) =>
      data((_, data) =>
        fs.writeFile(filepath, data, callback)));

The error handling is skipped to keep the code simple.

It is now possible to read one file and save its content to another by calling asynchronous read and write similarly to their synchronous counterparts:

write(value('file.out'), read(value(''))();

The trailing () is not a mistake - it invokes write.

Constructing a library of async functions wrapped following this pattern makes possible to write code using these functions like if there are synchronous:

var tokens = readTokensFromDb(value('user'));
var html = replaceTokens(read(value('template.html')), tokens);

where readTokensFromDb, read, replaceTokens, sendEmail are asynchronous.

  1. Node.js style callbacks, ↩︎