Skip to content

Instantly share code, notes, and snippets.

@cpdean
Created January 27, 2014 23:36
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cpdean/8659630 to your computer and use it in GitHub Desktop.
Save cpdean/8659630 to your computer and use it in GitHub Desktop.
q.js demo for merging the results of an arbitrary number of promises generated by an initial promise.
// @cpdean
// demonstrates how to have one promise kick off
// an additional arbitrary number of promises, and
// then merge their results down again after
// all the promises are complete.
var Q = require('q'); // "q": "~1.0.0"
// initial query that generates seed data for more work to be done
function fakeQuery(){
var deferred = Q.defer();
setTimeout(function(){
console.log("first query complete");
deferred.resolve([{id: 1}, {id: 2}, {id: 3}]);
}, 1e3);
return deferred.promise;
}
// second query
function deferredPropertyLookup(x) {
var deferred = Q.defer();
setTimeout(function(){
console.log("next query complete on " + x);
deferred.resolve(x);
}, 1e3);
return deferred.promise;
}
fakeQuery().then(function(results){
// `results` could be an arbitrary number of things
// to do more work on
// then concurrently run the lookup function with its
// post processing step in then()
var with_properties = results.map(function(e){
return deferredPropertyLookup(e.id).then(function(found){
return {id: e.id, prop: found};
});
});
return Q.all(with_properties).done(function(a){
// print the results when the lookups and processing are done
console.log(a);
});
});
@tsikov
Copy link

tsikov commented Jul 4, 2014

Great example! Thanks :)

There is one thing I cannot understand. It seems that the only reason you use fakeQuery is to pass the array of objects to then. Why not just use Q([{id: 1}, {id: 2}, {id: 3}]) instead?

@DiegoYungh
Copy link

I lost hours trying to reproduce what they already have!

I wanted to execute promises and non promises in the order and get all the values in the end, so I did this snippet:

http://jsbin.com/vumakumuqu/5/edit

function createGetValue(value) {
    return function() {
        var q = Q.defer();

        Q.delay(1000).then(function() {
            q.resolve(value);
        });

        return q.promise;
    };
}

var dict = {};

function valueCollector(identifier, fun) {
    return function() {
        var result;

        result = fun.apply(this, arguments);

        if (result.hasOwnProperty('inspect') === false)
            result = Q(result);

        return result.then(function(value) {
            dict[identifier] = value;
            return dict;
        });

    };
}

var a, b, c, d;

a = {
    identifier: 'a',
    getValue: createGetValue('value of a!')
};

b = {
    identifier: 'b',
    getValue: createGetValue('value of b!')
};

c = {
    identifier: 'c',
    getValue: function() {
        return 'value of c, no promisses here!';
    }
};

d = {
    identifier: 'd',
    getValue: createGetValue('value of d!')
};

_.reduce(
    [a, b, c, d],
    function(promiseStack, field) {
        field.getValue = valueCollector(field.identifier, field.getValue);
        if (promiseStack === null) {
            return field.getValue();
        } else {
            return promiseStack.then(field.getValue);
        }
    },
    null
).then(function(data) {
    console.log(data);
})
.fail(function() {
    console.log('fuck!');
});

// Returns
//
//  {
//      a: "value of a!",
//      b: "value of b!",
//      c: "value of c, no promisses here!",
//      d: "value of d!"
//  }

When in the end I could do pratically the same with:

Q.all([a.getValue(), b.getValue(), c.getValue(), d.getValue()]).done(function(result){
  console.log(result);
});

// Returns
// 
// ["value of a!", "value of b!", "value of c, no promisses here!", "value of d!"]

To get the same result, I will just use the same wrapper to return objects instead of values only, and merge then all into one, end of story!

Documentation needs refactoring, also some graphical information, promise system and recursion are better visualized in images I think.

Cheers!

@GasCreature
Copy link

Great example! One that actually works when copy/pasted into a file.

There are some people who think including a line like
var Q = require('q');
is too much distraction.

@neodon
Copy link

neodon commented Sep 4, 2015

Here is a snippet I use when I need to run an arbitrary number of promises one at a time sequentially. This is handy if you are making a lot of API calls and don't want to exceed rate or concurrent request limits.

function runPromiseSequence(items) {
  return items.map(function(item) {
    // Have to wrap this in a function to generate a promise for each item
    return function() {
      // return a value or promise
      return item.doSomethingAsync();
    };
  }).reduce(Q.when, Q());
}

The function will return a promise chain for each item in the array, like ...then(function(item[0] { ... }).then(function(item[1]) { ... }) etc and you can add to the end of the chain to do something after all the other promises are resolved sequentially.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment