chaining async method calls - javascript

Warning this isn't quite right yet. Ideally we'd subclass Promise and have proper then/catch functionality but there are some caveats with subclassing bluebird Promise. The idea is to store an internal array of promise generating functions, then when a Promise is waited on (then/await) serially wait on those promises.

const Promise = require('bluebird');

class Foo {
  constructor() {
    this.queue = [];
  }

  // promise generating function simply returns called pGen
  pFunc(i,pGen) {
    return pGen();
  }

  bar() {
    const _bar = () => {
      return new Promise( (resolve,reject) => {
        setTimeout( () => {
          console.log('bar',Date.now());
          resolve();
        },Math.random()*1000);
      })      
    }
    this.queue.push(_bar);
    return this;
  }

  baz() {
    const _baz = () => {
      return new Promise( (resolve,reject) => {
        setTimeout( () => {
          console.log('baz',Date.now());
          resolve();
        },Math.random()*1000);
      })      
    }
    this.queue.push(_baz);
    return this;
  }

  then(func) {
    return Promise.reduce(this.queue, this.pFunc, 0).then(func);
  }
}


const foo = new Foo();
foo.bar().baz().then( () => {
  console.log('done')
})

result:

messel@messels-MBP:~/Desktop/Dropbox/code/js/async-chain$ node index.js 
bar 1492082650917
baz 1492082651511
done

If you want to avoid callback hell and keep your sanity ES6 promises are the most appropriate approach for the sake of functional programming. You just chain up your sequential asynchronous tasks in the asynchronous timeline just like working in a synchronous timeline.

In this particular case you just need to promisify your asynchronous functions. Assume that your asynch functions takes a data and a callback like asynch(data,myCallback). Let us assume that the callback is error first type.

Such as;

var myCallback = (error,result) => error ? doErrorAction(error)
                                         : doNormalAction(result)

When your asynch function is promisified, you will actually be returned a function which takes your data and returns a promise. You are expected to apply myCallback at the then stage. The return value of myCallback will then be passed to the next stage at where you can invoke another asynch function supplied with the return value of myCallback and this goes on and on as long as you need. So let's see how we shall implement this abstract to your workflow.

function Foo(){}

function promisify(fun){
  return (data) => new Promise((resolve,reject) => fun(data, (err,res) => err ? reject(err) : resolve(res)));
}

function myCallback(val) {
  console.log("hey..! i've got this:",val);
  return val;
}

var bob = new Foo();

Foo.prototype.bar = function land(value, callback) {
  setTimeout(function() {
    callback(false,value*2);  // no error returned but value doubled and supplied to callback
    console.log('bar');
  }, 1000);
};

Foo.prototype.baz = function land(value, callback) {
  setTimeout(function() {
    callback(false,value*2);  // no error returned but value doubled and supplied to callback
    console.log('baz');
  }, 1000);
};

Foo.prototype.bar = promisify(Foo.prototype.bar);
Foo.prototype.baz = promisify(Foo.prototype.baz);

bob.bar(1)
   .then(myCallback)
   .then(bob.baz)
   .then(myCallback)

The more recommended way instead is to use promises as this is the community-wide practice to do async work.

We want to do bob.bar().baz() and have it log "bar" and "baz" sequentially.

Why would you want to do that just to achieve this bob.bar().baz() "syntax"? You could do it pretty neatly using the Promise API w/o additional efforts to make that syntax work that indeed increases code complexity reducing the actual readability.

So, you might want to consider using the promise-based approach like this. It offers much flexibility than what you would have achieved with your approach:

Foo.prototype.bar = function () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve()
            console.log('bar');
        }, 3000);
    };
};

Foo.prototype.baz = function () {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve()
            console.log('baz');
        }, 3000);
    };
};

Now you'd do this to run them sequentially one after another:

var bob = new Foo();

bob.bar().then(function() {
   return bob.baz();
});

// If you're using ES2015+ you could even do:
bob.bar().then(() => bob.baz());

If you need to chain more functions you could simply do it:

bob.bar()
    .then(() => bob.baz())
    .then(() => bob.anotherBaz())
    .then(() => bob.somethingElse());  

Anyway, if you're not used to using promises you might want to read this