Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass extra data down a Parse Promise chain [duplicate]

In my Parse Cloude code I need to run a few successive queries, each of them using a "find()".

Example:

var promise = firstQuery.get(objectId).then(function(result1){
            return secondQuery.find();
        }).then(function(result2){
            return thirdQuery.find();
        }).then(function(result3) {

             // here I want to use "result1", "result2" and "result3"
        });

The question is: how do I access "result1" and "result2" in the final "then" statement, without assigning them to variables declared in the parent scope.

Why do I ask this: You cannot use the parent scope trick if you are nesting a bunch of promises which you create in a loop in order for them to be executed in parallel (imagine a for loop around the above statement whereby all the promises are put in an array and then evaluated using "Parse.Promise.when". They would all start modifying the parent scope variables at the same time.)

Can I create some kind of promise object where I could return something along the lines of:

Parse.promise({result:result1,findResult:secondQuery.find()};

so I could get the values out of the "result2" parameter by doing

result2.result 

and

result2.findResult

I hope I make myself clear. This is not very easy to explain.

like image 874
Joris Mans Avatar asked Aug 12 '14 00:08

Joris Mans


2 Answers

You can make use of closures to do this, without the need for any extra objects or wrapping.

var promise = firstQuery.get(objectId).then(function(result1){
    return secondQuery.find()
    .then(function(result2) {
        return thirdQuery.find()
        .then(function(result3) {
            //can use result1, result2, result3 here
        });
    });
});

This "nested" syntax is identical in function to your "chaining" syntax.


EDIT based on comments

If your promise chain is long and complex enough that this nested syntax becomes ungainly, the logic is probably complex enough to merit abstraction into its own function.

function complexQuery(objectId) {
    var results = {};
    return firstQuery.get(objectId).then(function(result1) {
        results.result1 = result1;
        return secondQuery.find();
    })
    .then(function(result2) {
        results.result2 = result2;
        return thirdQuery.find();
    })
    .then(function(result3) {
        results.result3 = result3;
        return results;
    });
}

complexQuery(objectId)
.then(function (results) {
    //can use results.result1, results.result2, results.result3
});

Personally, I think that's easier to read and maintain than messing around with .bind.

like image 50
Retsam Avatar answered Oct 15 '22 17:10

Retsam


You cannot use the parent scope trick

Well, since both the other answers do this, let me suggest a solution that doesn't. You can pass resolved promises along. This also has the added benefit of no nesting or closures.

This is based on the concept that promises are proxies for values already, so you don't actually have to create one long chain:

var firstObject = firstQuery.get(objectId);
var secondObject = firstObject.then(secondQuery.find.bind(secondQuery));
var thirdObject = secondObject.then(thirdQuery.find.bind(thirdQuery));
Promise.all(firstObject, secondObject, thirdObject).then(function(r1, r2, r3){
   // here you can use "r1", "r2" and "r3"
});

In standard promises, rather than parse code this would look similar:

Promise.all([firstObject, secondObject, thirdObject]).then(function(){
   var r1 = arguments[0], r2 = arguments[1], r3 = arguments[2]; 
   // here you can use "r1", "r2" and "r3"
});

With bluebird you can use .spread for a shortcut or .bind or actual context. If you absolutely must create one chain, you can pass context by returning multiple promises using Promise.all but I believe this approach is preferable.

like image 35
Benjamin Gruenbaum Avatar answered Oct 15 '22 18:10

Benjamin Gruenbaum