1

I swear by Q for its simplicity, so I might not have done much research checking out other 'then' implementations. But I've used Q a good deal!

I have a chain of 'then' promises, and I want to resolve a 'batch' of promises in the middle of it and proceed with other operations in sequence; so it's obvious I should use Q.all. But I'm stuck here. Either Q's doing it wrong, or I am.

Here are two hypothetical async operations

var f=function(delay){
  return Q.delay(delay).then(function(){
    console.log("returning delayed",delay)
    return delay
  })
}

f2=function(delay){
  var defer=Q.defer()
  setTimeout(function(){
    console.log("returning timedout",delay)
    return delay
  },delay)
  return defer.promise
}

And this is the promise chain

  Q('begin')
    .then(console.log)
    .then(Q.all([100,200,300,400].map(f)))
    .then(function(){
      console.log("Finally",arguments)
    }).done()

And this is the output I wish

begin
returning delayed 100
returning delayed 200
returning delayed 300
returning delayed 400
Finally { '0': undefined }

But this is the output I get

begin
Finally { '0': undefined }
returning delayed 100
returning delayed 200
returning delayed 300
returning delayed 400

I get the same sequence with f2

Now, if I run this

Q.all([100,200,300,400].map(f)).then(function(){
  console.log("Finally",arguments)
}).done()

I do get

returning delayed 100
returning delayed 200
returning delayed 300
returning delayed 400
Finally { '0': [ 100, 200, 300, 400 ] }

but using f2 instead of f gives me

returning timedout 100
returning timedout 200
returning timedout 300
returning timedout 400

It doesn't execute the finally block.

I get the same outputs with Q.[all/allResolved/allSettled]

So, my queries are,

  1. How do I achieve the intended output by using Q.all specifically. I do have a workaround though, but it doesn't look nice.
  2. How are f and f2 different since the results by running Q.all().then() with them aren't the same.
Cœur
  • 37,241
  • 25
  • 195
  • 267
aksci
  • 150
  • 1
  • 2
  • 10

2 Answers2

3

Q.all takes an array of promises, but then returns a promise (that is the only resolved when every promise in the array is resolved.

So, I think you need to return the result of Q.all in order to not immediately call the next then:

Q('begin')
    .then(console.log)
    .then(function() {
        return Q.all([100,200,300,400].map(f));
    })
    .then(function(){
      console.log("Finally",arguments)
    }).done()
Davin Tryon
  • 66,517
  • 15
  • 143
  • 132
2

Answering the second question, your usage of f2 is incorrect.

When you create a promise using a deferred you must resolve it in order for it to become fulfilled. You can read more about it in How to convert a callback API to promises.

In your case you're creating an empty deferred and returning its promise but you never actually call .resolve on the deferred. Doing it "more correctly" would be something like:

f2=function(delay){
  var defer=Q.defer()
  setTimeout(function(){
    console.log("returning timedout",delay)
    defer.resolve(delay); // note the resolve here vs the return
  },delay)
  return defer.promise
}

Using Q.delay works better since it's already promisified. As for other then implementations Q is hardly bleeding edge right now to say the least :)

Community
  • 1
  • 1
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504