-1

I have a JavaScript object with unresolved promises within. What would be the most elegant and simple way to resolve all of these promises within the object and continue to next 'then' block only when all of them are resolved?

var Promise = require('bluebird')
var _ = require('lodash')

Promise.all(fetchSomeData)
.then(data => {
    /* 'data' contents:
    [
        { 
          foo: 'foo1', 
          bar: 'bar1', 
          datatofetch: new Promise((resolve,reject) => {...}) 
        },
        { 
          foo: 'foo2', 
          bar: 'bar2', 
          datatofetch: new Promise((resolve,reject) => {...}) 
        },
        { 
          foo: 'foo3', 
          bar: 'bar3', 
          datatofetch: new Promise((resolve,reject) => {...}) 
        }
        ...
   ]
   */

   return // TODO: 'data' object with resolved promises
})
.then(dataWithResolvedPromises => {
    // do something with dataWithResolvedPromises
})
Robert
  • 457
  • 7
  • 19
  • What values should promises be resolved with? What is purpose of including unresolved `Promise` as property each object? – guest271314 Apr 10 '16 at 20:51
  • `Promise.map(Promise.props, data)`? – Bergi Apr 10 '16 at 21:00
  • @Bergi How would `Promise` constructor be resolved without first passing `resolve` method outside scope of `Promise` constuctor? – guest271314 Apr 10 '16 at 21:19
  • 1
    @guest271314: I don't think OP is asking for how to resolve those promises, rather about how to await them so that their results become available together. He just is stating that they are still unresolved when he gets the `data`. – Bergi Apr 10 '16 at 21:25
  • How would those promises ever be resolved without passing `resolve` outside scope of `Promise` constructor pattern? Which is not addressed at actual Question? How would those results be retrievable if the promise is neither resolved or rejected? – guest271314 Apr 10 '16 at 21:28
  • @Robert `js` at original Question appears different from updated `js` at Question? _"Bergi suggested to use Promise.props and I did and it worked really well"_ You can post your solution as an Answer, and possibly accept your own Answer. – guest271314 Apr 10 '16 at 21:54

2 Answers2

2

You can use two different Promise.all calls and simply map the result of the second back onto the result of the first once resolved

var Promise = require('bluebird')
var _ = require('lodash')

var myObjects
Promise.all(fetchSomeData)
.then(data => {
    myObjects = data;
    return Promise.all(data.map(d => d.datatoFetch)
})
.then(fetched => fetched.map((result, i) => _.extend({}, myObjects[i], { dataToFetch: result })))
.then(dataWithResolvedPromises => {
    // do something with dataWithResolvedPromises
})
Charlie Martin
  • 8,208
  • 3
  • 35
  • 41
  • It's not global. He is using require, so the module will be wrapped. – Charlie Martin Apr 10 '16 at 21:06
  • Sorry, to be accurate I should've said "Please avoid mutable free variables in promise callbacks". There are so many so much better ways to achieve the desired result. – Bergi Apr 10 '16 at 21:11
  • @Bergi With my simple edit, I'm no longer mutating anything. This is purely functional now. I'm just making use of javascript closures, which is an excellent feature of most languages. Please post an answer showing one of the many better ways to do this you claim to know – Charlie Martin Apr 10 '16 at 21:13
  • I was refering to `myObjects = data`, not the `fetched` array. See the question I linked for all possible approaches to deal with this. – Bergi Apr 10 '16 at 21:20
  • @Bergi I know. I am no longer mutating `myObjects` (or any objects at all). I'm just saving the result of the first `Promise.all` in a higher scope so its available in multiple closures below it. This is an extremely common pattern in most languages – Charlie Martin Apr 10 '16 at 21:21
  • Yes, that's exactly what I consider problematic. You are mutating the variable, it is an imperative approach not a functional one. Sure, it's common, it's simple, that doesn't mean it's good :-) – Bergi Apr 10 '16 at 21:23
  • @Bergi Once again. I am not mutating anything at all. Once `myObjects` is assigned, it is only read and never again written. I understand what you think is bad, but I'm not doing it here – Charlie Martin Apr 10 '16 at 21:24
  • Does your solution resolve `Promise` passed within `Promise` constructor pattern as appears at OP? If yes, can you create a stacksnippets or plnkr http://plnkr.co to demonstrate? – guest271314 Apr 10 '16 at 21:24
  • @CharlieMartin: It's not initialised where it is declared, you have to assign (mutate) it once, which can be error-prone. You couldn't use `const` there instead of the `var`. – Bergi Apr 10 '16 at 21:29
  • @Bergi There is nothing wrong with declaring variables and assigning them later. This is a feature of closures which allows you to solve problems like the one in his question. It allows you to "hoist" a value out of a lower closure into a higher one making it available to all siblings of the originating closure. It is kind of like the `var that = this` trick that is common in javascript. Once again, please post an answer and show how you would accomplish this otherwise – Charlie Martin Apr 10 '16 at 21:31
  • @CharlieMartin fwiw, your interpretation of Question is apparently correct. – guest271314 Apr 10 '16 at 21:59
0

Using Promise.props in conjunction with Promise.all seems the most simple way to solve this.

Promise.all(fetchSomeData)
.then(data => {
    return Promise.map(data, Promise.props)
})
.then(dataWithResolvedPromises => {
    // do something with dataWithResolvedPromises
})
.catch(err => {
    console.log(err)
})
Robert
  • 457
  • 7
  • 19