Shih-Min Lee's Personal website

dating, chating, food, games, search

Follow me on GitHub

😀 bluebird.js

Callbacks

In Node.js you can do async stuff using callbacks. A callback is a function inside a function call. For example db read/writes or file io are very slow. In this case we can send a callback function as parameters to a function and the CPU can go do something else while we’re waiting for the result of the function to comeback before going to the next functions.

fs.readFile('A.txt', function(err, d1){ // ... 1
  if (err1) {
    // do something
  }
  fs.readFile(d1 + '.txt', function(err, d2){ // ... 2
    if (err2) {
      // do something
    }
    fs.readFile(d2 + '.xls', function(err, d3){ // ... 3
      if (err3) {
        // do something
      }
      // finally do something
    });
  });
})

The idea is to read A.txt, then use the stuff inside A.txt to read B.txt, and then C.xls before we finally does something else.

As you can see this nesting function calls look really messy after a while. Also the error handing here is minimal. You know where the error occurs and you can decide what to do with errors. But it’s just hard and messy.

Besides if you want to do something more complicated like this you wouldn’t even know how to start:

if err3 then redo 2, if err2 redo 1, if err3 ten times than do something else.

That when we switch to promises.

Promises

Promises are great in that it lets you write code in a very clean way. You can also do the error handling in a way that other people can read. The above code can basically be written as follows:

var Promise = require('bluebird'); // official documentation use Promise (capital letter. so don't change it)
var fs = Promise.promisifyAll(require("fs"));

fs.readFile('A.txt') --- (1)
.then(function(d){
  return fs.readFile(d + '.txt'); --- (2)
}).then(function(d){
  return fs.readFile(d + '.xls'); --- (3)
}).catch(function(err){
  // do something with the error, the error could come from code block 1 or 2 or 3
});

to do the error handing described above you can (maybe) do something like this:

var count = 0;
var a, b, c;

var A = fs.readFileAsync('A.txt')
var B = function(d){
  a = d.toString();
  return fs.readFileAsync(d.toString() + ".txt");
}
var C = function(d){
  b = d.toString();
  return fs.readFileAsync(d.toString() + ".xls");
}
var D = function(err){
  count += 1;
  if (count > 100) {
    return Promise.reject('failed to load this stuff')
  } else if (err1) {
    return ABCD();
  } else if (err2) {
    return BCD(b);
  } else if (err) {
    return CD(c);
  } else {
    console.log('there is a weird error somehow...')
  }
}
var ABCD = function(){
  return A.then(B).then(C).catch(D);
}
var BCD = function(d){
  return Promise.resolve(d + ".txt").then(B).then(C).catch(D);
}
var CD = function(d){
  return Promise.resolve(d + ".xls").then(C).catch(D);
}

And the code becomes

A
.then(B)
.then(C)
.catch(D);

or

ABCD()

This is a pseudo code. you need to sort out the technicalities by your self.

Common functions that I use

  • bind()

It’s basically similar to Javascript .bind() where you can bind a context to become this

  • resolve()
  • reject()

Basically I convert all objects to a promifified object to ensure consistency. So the resolve is an resolved or rejected promise.

  • settle()

When you deal with multiple promises some of them might fail but you want your promise chains to continue to execute without throwing errors and stopping the loop.

  • map()
  • reduce()

It’s pretty much just like Javascript Array.prototype.map() except it’s dealing with an array of promises. For map() you can also send in concurrency as optional params.

  • Promise.promisifyAll()
  • Promise.promisify()
  • Promise.cast()

important when you want to cast other types of promises to a bluebird promise. Basically when I do promises I cast all kind of promises (such as mongoose promise) to a bluebird promise to ensure there’s no race conditions. This is deprecated to Promise.resolve I think.

  • spread()

I also use this to reduce the code.

in situations we have blocks of code like this

var aaa = function(){
  return [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)]
}
var bbb = function(a, b, c){
  // do something
}

and then you can simplify the following code

aaa()
.then(function(d){
  return bbb(d[0], d[1], d[2])
})

to

aaa()
.spread(bbb)

maybe some people find this useful I don’t know.

Miscellaneous

  • I think Promise.map method can have maximum concurrency of 5. If there are more than 5 promises running at the same time it won’t work. So a lot of times I just set
{
  concurrency: 1
}

when I am using Promise.map method.

references:

18 Jul 2015