-2

Currently I am trying to call multiple awaits within a for loop, as per the documentation this is a performance heavy, so I was thinking of using promise.all() but the problem I am facing is I need the first awaits data to call the other awaits values with in the for loop and assign the new values in an empty object that I created outside the for loop. Do you know how to use promise.all() to solve this problem?

This is my current code:

      const parsedSchema = {}
      const arrayOfValues = Object.keys(objectOfValues);
      for (let i = 0; i < arrayOfValues.length; i++) {
        const arrayOfValuesSchema = (
          await getObjectFromExternalAPI(arrayOfValues[i])
        ).data;
    
        Object.assign(parsedSchema, {
          ...(await $RefParser.dereference(JSON.parse(arrayOfValuesSchema.toString('utf-8')))).properties
        });
      } 

Update: https://jsfiddle.net/sy4j6mgu/ this worked for me but I don't know how to simplify from here.

Vikram
  • 7
  • 3
  • Do all the awaited values depend on the previous awaited value? Or just the second value needs the first? – mikemaccana Oct 17 '22 at 21:45
  • Idea: `await` the first value (which you say is needed by every other call). Then use `Promise.all` or `Promise.allSettled` to fire off the remaining requests in parallel; then aggregate the results. – Ben Aston Oct 17 '22 at 21:47
  • @mikemaccana for each iteration the second await value has to get the data from the first await in-order to fetch the values. – Vikram Oct 17 '22 at 21:48
  • ...and the first await needs only to be run once in total, or once per time around the loop? – Ben Aston Oct 17 '22 at 21:49
  • @BenAston could you please elaborate in code form, if possible, thank you. – Vikram Oct 17 '22 at 21:50
  • @BenAston first await need to run each and every time the loop iterates, I have updated the code little bit please take a look. – Vikram Oct 17 '22 at 21:51
  • 1
    This looks like a case of [the XY problem](https://xyproblem.info) to me -- what are you trying to accomplish? Why call `toString` on `arrayOfValuesSchema` -- what is the type of latter? `JSON.parse` does not return a promise and thus `await` ahead of its return value isn't needed. `...` between `{` and `}` without any other properties being defined on the object, is just copying the object that is the operand for `...` -- is there any benefit to this copying in your case? Why `Object.assign` _in addition_ to the copying? – Armen Michaeli Oct 17 '22 at 21:52
  • there seems to be a lot of unnecessary... things in your code, that if removed, your code should work as you've described you intended it to. – Kevin B Oct 17 '22 at 21:54
  • 1
    @KevinB currently it is working as intended but the performance is taking a toll, I am searching for a solution that could be done with promise.all(). Please let me know what code to remove and add. – Vikram Oct 17 '22 at 21:59
  • I see, that simplifies things quite a bit, we've got a duplicate for that. what you're looking for is feeding the result of `arrayOfValues.map` to promise.all, resulting in all of them being processed in parallel, and then the code continues once they're all done. You might need to chunk things though, if your external api has any throttling. – Kevin B Oct 17 '22 at 22:00
  • @Vikram so your question is really “how can I do this faster “ rather than “how can I do this”. I’d use an asyncMap (you can find one on npm) and then just have two awaits inside the function, so that each iteration waits for a before awaiting b. – mikemaccana Oct 17 '22 at 22:02
  • The "parallel" version of this answer: https://stackoverflow.com/a/37576787/400654 – Kevin B Oct 17 '22 at 22:03
  • @KevinB I am still having tough time writing the parallel version for my solution: ` const parsedSchema = {} const arrayOfValues = Object.keys(objectOfValues); const promises = [] for (let i = 0; i < arrayOfValues.length; i++) { const arrayOfValuesSchema = promise.push(( getObjectFromExternalAPI(arrayOfValues[i]) ).data); await promise.all(promises) Object.assign(parsedSchema, { ...(await $RefParser.dereference(JSON.parse(arrayOfValuesSchema.toString('utf-8')))).properties }); } ` – Vikram Oct 17 '22 at 22:16

1 Answers1

0

Does this help?

The following will synchronously and rapid-fire create promises for each value in arrayOfValues. Each executor function runs synchronously and tees-up async requests to getObjectFromExternalAPI and then $RefParser.dereference on the result.

The code will wait for all the promises to be fulfilled. Control then moves to synchronous aggregation of the results into a single object using Array#reduce.

const arrayOfValues = Object.keys(objectOfValues)

const promises = arrayOfValues.map((v) => new Promise((resolve) =>
    getObjectFromExternalAPI(v)
        .then(({ data }) => $RefParser.dereference(data.toString('utf-8')))
        .then(resolve)

const results = await Promise.all(promises) // fails fast!

const parsedSchema = results.reduce((acc, { properties }) => 
    ({ ...acc, ...properties }), {})
Ben Aston
  • 53,718
  • 65
  • 205
  • 331
  • declaration or statement expected at const parsedSchema = results.reduce((acc, { properties }) => { ...acc, ...properties }, {}) – Vikram Oct 17 '22 at 22:42
  • Updated. Remember: I’m coding blind here! – Ben Aston Oct 17 '22 at 22:48
  • I really apricate it. Thank you. I will keep you updated. – Vikram Oct 17 '22 at 22:55
  • I keep getting 'TypeError: Cannot read property 'then' of undefined\n, but I can see the getObjectFromExternalAPI(v) values being fetched. – Vikram Oct 17 '22 at 23:40
  • Confirm $RefParser.dereference returns a promise. – Ben Aston Oct 18 '22 at 06:22
  • https://jsfiddle.net/sy4j6mgu/ please check the link and this is working for me but please let me know how to condense it and make it easier for other developers to understand. – Vikram Oct 18 '22 at 18:33