Introduction to Promises

What is "callback hell"

Javascript that uses callbacks, is hard to get right intuitively. A lot of code ends up looking like this:

callback hell

fs.readdir(source, function(err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function(filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function(err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function(width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(
                destination + 'w' + width + '_' + filename, function(err) {
                  if (err) console.log('Error writing file: ' + err)
                }
            )
          }.bind(this))
        }
      })
    })
  }
})

So, how do we solve callback hell?

Promises!

Promise is used for deferred and asynchronous computations. A Promise represents an operation that hasn't completed yet, but is expected in the future.

Promise is in one of these states:

  • Pending
    • Initial state, not fulfilled or rejected.
  • Fulfilled
    • ​Meaning that the operation completed successfully.
  • Rejected
    • ​Meaning that the operation failed.
// You should use promises to turn this:

fs.readFile("file.json", function (err, val) {
    if (err) {
        console.error("unable to read file");
    }
    else {
        try {
            val = JSON.parse(val);
            console.log(val.success);
        }
        catch (e) {
            console.error("invalid json in file");
        }
    }
});
// Into this:

fs.readFileAsync("file.json").then(JSON.parse).then(function (val) {
    console.log(val.success);
})
.catch(SyntaxError, function (e) {
    console.error("invalid json in file");
})
.catch(function (e) {
    console.error("unable to read file");
});

As the Promise.prototype.then() and 
Promise.prototype.catch() methods return
promises, they can be chained

Creating a Promise

// We use new Promise to construct the promise. 
// We give the constructor a factory function 
// which does the actual work.

function readFile(filename, enc){
  return new Promise(function (resolve, reject){
    fs.readFile(filename, enc, function (err, data){
      if (err) reject(err);
      else resolve(data);
    });
  });
}

The passed in function will receive functions resolve and reject as its arguments which can be called to seal the fate of the created promise.

Resources

Intro to Promises

By Joe Karlsson

Intro to Promises

  • 2,024