maephisto
maephisto

Reputation: 5182

Javascript asynchronous calls chaining

Given an array which contains objects of type A and B, where B can be transformed to a set of A type objects, via an asynchronous call, which would be the best way of transforming the array into an all A objects array (transforming each B object in a set of corresponding A objects) and execute a callback when Bs are transformed?

    list = [A, B, A, B, A, A, B, A];

    function transform (B, callback) {
       //transform B type object into A type objects array [A, A, A].....
       callback([A, A, A]);
    }

    transformBObjectsIntoAObjects(list, callback) {
       // ?????????
      callback(list); // this should only be A type objects
    }

Upvotes: 1

Views: 85

Answers (2)

closure
closure

Reputation: 7452

Here is a complete working example with async:

var async = require("async")

function A (id) {
  this.id = 'A' + id;
}

function B (id) {
  this.id = 'B' + id;
}

var list = [new A(1), new B(2), new A(3), new B(4)];

function transformBObjectsIntoAObjects (b, callback) {
   var ar = [], count = Math.random() * 5;
   for (var i = 1; i <= count; i++)
     ar.push(new A(b.id + "_" + i))
   return callback(null, ar);
}

async.map(list, function(arItem, cb) {

  return (arItem.constructor === B) ? transformBObjectsIntoAObjects(arItem, cb) : cb(null, arItem)

  }, function (err, arResult) {
    var flatAr = [].concat.apply([], arResult);
    console.log(flatAr);
  }
)

One such result (B parts are randomly generated) looks like:

[ { id: 'A1' }, { id: 'AB2_1' }, { id: 'AB2_2' }, { id: 'A3' }, { id: 'AB4_1' } ]

Upvotes: 1

Bergi
Bergi

Reputation: 664164

Well, you need to execute your final callbacks after all the callbacks from transformBtoList have returned a result. There are multiple ways to do it:

  1. Count how many callbacks you have passed away and decrement when they call back, and you know that you're finished when the counter has reached zero again.

    However, this is cumbersome, and there are libraries that help you with it:

  2. async.js is well-known and easy to use:

    function transform(B, callback) { … }
    function transformBObjectsIntoAObjects(list, callback) {
      async.map(list, function(X, cb) {
        if (X is B)
          transform(X, cb)
        else
          cb([X])
      }, function(results) {
        callback(concat(results))
      }
    }
    
  3. Promises (there are many implementations are a superior approach. They might be a bit more complex to understand, but have very nice properties and lead to nice & concise syntax. In your case, it would be

    function transform(B) { // no callback!
      // async:
        resolve([A, A, A]); // see docs of your promise library
      return promise;       // for exact reference
    }
    function transformBObjectsIntoAObjects(list) {
      return Promise.all(list.map(function(X) {
        return (X is B) ? transform(X) : Promise.resolve([X]);
      })).then(concat);
    }
    

Upvotes: 2

Related Questions