0

I have three queries on Firestore based on a time range. (24, 12 and 6 hour). I am using Promise.all and it works. As you can see from the code, I am accessing the result of each query by using an index to the returned snapshot. I have read that the Returned values will be in the order of the Promises passed, regardless of completion order.

Now, I want to be able to pass an object to the Promise.all because my number of queries will be unpredictable for what I want to do, basically, I will be looping to a number of vehicles and building the same 3 queries for each and I will pass it all on a Promise.all. And when Promise.all returns, I want to be able to know which vehicle and time range that snapshot is for.

So instead of an array, I want to pass this argument to Promise.all instead.

{"vehicle1_24":query, "vehicle1_12":query, "vehicle1_6":query,
"vehicle2_24":query, "vehicle2_12":query, "vehicle2_6":query}

code

var queries = [
        vehicleRef.collection('telemetry').where('time_stamp', '<', today).where('time_stamp', '>', yesterday).get(),
        vehicleRef.collection('telemetry').where('time_stamp', '<', today).where('time_stamp', '>', twelveHours).get(),
        vehicleRef.collection('telemetry').where('time_stamp', '<', today).where('time_stamp', '>', sixHours).get()
      ]

      for (var i = 0; i < queries.length; i++) {
        queryResults.push(
          queries[i]
        )
      }

      Promise.all(queryResults)
        .then(snapShot=> {

          const yesterdayResult = result => getEnergy(result);
          const twelveHourResult = result => getEnergy(result);
          const sixHourResult = result => getEnergy(result);

          allYesterdayResult += yesterdayResult(snapShot[0])
          allTwelveHourResult += twelveHourResult(snapShot[1])
          allSixHourResult +=sixHourResult(snapShot[2])


          console.log("Done updating vehicle ", vehicle)
          // return res.send({"Result" : "Successful!"})
        }).catch(reason => {
          console.log(reason)
          // return res.send({"Result" : "Error!"})
ellaRT
  • 1,237
  • 2
  • 16
  • 38
  • `Promise.all()` needs an array. If you want to wait for a bunch of promises that are all properties on an object, then you have to extract all those promises from the object and put them in an array so you can call `Promise.all()` with the array. You can make a function `Promise.allObject()` that does that for you if you want. – jfriend00 Apr 24 '18 at 04:56
  • FYI, the [Bluebird promise library](http://bluebirdjs.com/docs/api/promise.props.html) has `Promise.props()` that sounds like it might do what you want. – jfriend00 Apr 24 '18 at 04:59
  • what do you mean by creating my own `allObject()`? @jfriend00 – ellaRT Apr 24 '18 at 05:35
  • 1
    I mean you create your own function to do what you're asking for. It would be a function that extracts promises from properties on an object, puts them in an array, calls `Promise.all()` on them all, when `Promise.all()` resolves, then it processes the results however you want and returns that as the resolved value. Just make your own function that accepts the object you want to pass in and then converts the data to the right form to use `Promise.all()` because you can't pass the object to `Promise.all()` directly. – jfriend00 Apr 24 '18 at 05:46

2 Answers2

5

This feature does not exist natively, but should be fairly easy to write, something along the lines of

async function promiseAllObject(obj) {
  // Convert the object into an array of Promise<{ key: ..., value: ... }>
  const keyValuePromisePairs = Object.entries(obj).map(([key, valuePromise]) =>
    valuePromise.then(value => ({ key, value }))
  );

  // Awaits on all the promises, getting an array of { key: ..., value: ... }
  const keyValuePairs = await Promise.all(keyValuePromisePairs);

  // Turn it back into an object.
  return keyValuePairs.reduce(
    (result, { key, value }) => ({ ...result, [key]: value }),
    {}
  );
}

promiseAllObject({ foo: Promise.resolve(42), bar: Promise.resolve(true) })
  .then(console.log); // { foo: 42, bar: true }
Madara's Ghost
  • 165,920
  • 50
  • 255
  • 304
-1

You can use the following code to transform your object into an array that you will pass to Promise.all()

var queriesObject = {"vehicle1_24":query, "vehicle1_12":query, "vehicle1_6":query, "vehicle2_24":query, "vehicle2_12":query, "vehicle2_6":query};
//Of course, queriesObject can be an oject with any number of elements 


var queries = [];

for (var key in queriesObject) {
    if (queriesObject.hasOwnProperty(key)) {
        queries.push(queriesObject[key]);
    }
}

Promise.all(queries);

You will receive the results of Promise.all in an array corresponding to the fulfillment values in the same order than the queries array, see: Promise.all: Order of resolved values and https://www.w3.org/2001/tag/doc/promises-guide#aggregating-promises

Renaud Tarnec
  • 66,768
  • 8
  • 72
  • 104
  • @essramos You have to pass an iterable to Promise.all(), see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all and also https://medium.com/@ma7moudfelfel/introduction-to-javascript-iterables-iterators-and-generators-a26be413dfd9. Your object is not an iterable as such. It is what jfriend00 told you in his comment. One easy way to transform it in an iterable is to transform it as an array, as I detailed in my answer. In addition I don't understand why I have been downvoted: I would appreciate any explanation in such a way I improve myself. – Renaud Tarnec Apr 24 '18 at 08:10
  • 1
    I am not sure why you were downvoted. But your answer isn't what I need. I know passing an object isn't natively possible that is why I was asking a way how to do that. Not how to convert an object to array. – ellaRT Apr 24 '18 at 08:21
  • @essramos Thanks for your anwser. – Renaud Tarnec Apr 24 '18 at 08:23