Writing a promise library - Part 1

A simple promise implementation has four main methods.

  1. resolve - signal to the promise that the value it was waiting for now exists and some callback can be called with the value
  2. reject - signal that there was an error computing the promise’s value and the error callback should be invoked
  3. then - a method for registering the callback that should be invoked when the promise resolves
  4. catch - a method for registering the error callback that should be invoked when the promise is rejected

The following code shows a simple implementation of these four methods.

var promiseMaker = function() {

  var promise = {
    callback: null,
    error: null
  };

  promise.resolve = function(data) {
    this.callback(data);
  };

  promise.reject = function(err) {
    this.error(err);
  };

  promise.then = function(fn) {
    this.callback = fn;
  };

  promise.catch = function(fn) {
    this.error = fn;
  };

  return promise;

};

This implementation doesn’t yet allow for chaining of multiple promises, but it can be used to promisify a single callback style function.

For example, the following code shows a promise version of fs.readFile that allows the callback to be defined in the then method.

var fs = require('fs');

var readFile = function(filename) {
  var promise = promiseMaker();
  fs.readFile(filename, function(err, contents) {
    if (err) {
      promise.reject(err);
    } else {
      promise.resolve(contents.toString());
    }
  });
  return promise;
};
$ echo "hello world" > test.txt
readFile('test.txt')
  .then(function(contents) {
    console.log(contents);
  });
## hello world

Similarly the call to then could be replaced with a call to catch to handle an error in reading the file.

readFile('doesnt_exist.txt')
  .catch(function(err) {
    console.log(err);
  });
## { [Error: ENOENT, open 'doesnt_exist.txt'] errno: 34, code: 'ENOENT', path: 'doesnt_exist.txt' }

Creating chainable promises will be done in part 2