Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

chain array of promises with bluebird

I'm a working my way with promises and I'm stuck with my use case. I have an array of transformer functions (each function is a promise and modifies some JSON structure).

Let me show some code.

Lets say this is my JSON structure (array)

var data = [{a: 1, b:2}, {a:3, b:4}]; 

transformFunction is definition of transform functions modifying the data a certain way. The two functions adds c and d property to the above JSON structure:

var transformFunctions = { // 

    transform1: function (data) {  // This function adds `c` property to each object from `a`
        return new Promise(function (resolve) {
             for (var i = 0; i < data.length; i++) {
                 data[i].c = data[i].a;
             }
             return resolve(data);
        })
    },

    transform2: function (data) {  // This function adds `d` property to each object from `c`
        return new Promise(function (resolve) {
             for (var i = 0; i < data.length; i++) {
                 data[i].d = data[i].c;
             }
             return resolve(data);
        })
    },
    ...
}

The from a UI user specifies which transformer functions he should use and in what order. Lets say he picked the normal order like this:

var userTransformList = ['transform1', 'transform2'];

The transform1 method should modify the data and the result should be passed to transform2 method.

I was looking at: Promise.all but it seems that it does not care for the order of the promises, and most important it needs to pass the previous result to the next promise.

like image 850
sensor Avatar asked Jun 09 '15 13:06

sensor


2 Answers

Note: As adeneo pointed out in the comments, use promises only if you are dealing with asynchronous code.

  1. Create an array of functions which are to be executed. And make sure that they all return a Promise.

  2. Then, you can use Promise.reduce to reduce the initial value to the transformed final value by returning the result of executing current promise returning function, on every iteration.

  3. Finally you can attach a then handler to get the actual value and a catch handler, just in case if the promises are rejected.

Lets say we have two transform functions like this.

Note: I am telling again. You should never use Promises with functions like these. You should use promises only when the functions you are dealing with are really asynchronous.

// Just add a property called `c` to all the objects and return a Promise object
function transform1(data) {
    return Promise.resolve(data.map(function(currentObject) {
        currentObject.c = currentObject.a + currentObject.b;
        return currentObject;
    }));
}

// Just add a property called `d` to all the objects and return a Promise object
function transform2(data) {
    return Promise.resolve(data.map(function(currentObject) {
        currentObject.d = currentObject.a + currentObject.b + currentObject.c;
        return currentObject;
    }));
}

Then you can transform the original value, like this

Promise.reduce([transform1, transform2], function (result, currentFunction) {
        return currentFunction(result);
    }, [{a: 1, b: 2}, {a: 3, b: 4}])      // Initial value
    .then(function (transformedData) {
        console.log(transformedData);
    })
    .catch(function (err) {
        console.error(err);
    });

Output

[ { a: 1, b: 2, c: 3, d: 6 }, { a: 3, b: 4, c: 7, d: 14 } ]
like image 134
thefourtheye Avatar answered Oct 23 '22 05:10

thefourtheye


You can chain Promises the way you always do: using .then().

Let's say you have these two transformations:

function increment(x) {
    return Promise.resolve(x + 1);
}

function double(x) {
    return Promise.resolve(2 * x);
}

In a real scenario, these would be doing asynchronous work. You could just:

increment(1).then(double)

But, you don't know the order or number of transformations. Let's put them into an array, and then() them one by one:

var transformations = [increment, double]

var promise = Promise.resolve(1);

for (var i = 0; i < transformations.length; i++)
  promise = promise.then(transformations[i]);

You can attach a catch() handler before you start, after you finish or even per-transformation.

This isn't efficient if you're going to apply hundreds of transformations. In that case, you should use reduce() as thefourtheye suggests in his answer.

like image 34
slezica Avatar answered Oct 23 '22 06:10

slezica