0

In my project (VUE + Vuex) I need to make some API requests simultaneously, according to some contents and then process the results.

The getters.api_props(key) function will return the method ('post', 'patch', 'delete') or false if there is no need for a request. It will also return the url and the object that is needed for the request.

The api method returns the request as a Promise using axios.

Here is my code so far:

var contents = {person: {...}, info: {...}}
var promiseArray = [];
for (var key in contents) {
  let [method, url, hash] = getters.api_props(key);
  if (method) { promiseArray.push(api[method](url, hash)) }
}

await Promise.allSettled(promiseArray).then((results) => {
  results.map(r => {
    // THE RESULTS WILL BE PROCESSED HERE like:
    // commit("save", [key, r])
    console.info(r)
  })
  }).catch(e => console.log('ERROR:::',e)).finally(commit("backup"))

The problem is that the results does not include the 'key' so the save method that is called cannot know where to save the results.

Can you propose a fix or a better solution?

g_ap
  • 63
  • 1
  • 7
  • 1
    Just push the promise `api[method](url, hash).then(r => [key, r])` to the array, then you have the info you need to pass to `commit` – Bergi Nov 06 '22 at 16:26
  • 1
    Btw, you surely meant to use `for (var key of contents)` [not `for (var key in contents)`](https://stackoverflow.com/q/500504/1048572) – Bergi Nov 06 '22 at 16:27
  • My mistake. Actually, `contents` is an object. I'll edit the post. @Bergi, I tried your solution. Still can't figure out how to get `key` in `then`. – g_ap Nov 06 '22 at 16:43
  • 1
    Oh right, you need to use [`const key` or `let key` not `var`](https://stackoverflow.com/q/750486/1048572), but otherwise, it's just in scope – Bergi Nov 06 '22 at 16:45
  • I found the solution. Thanks @Bergi – g_ap Nov 06 '22 at 17:32

3 Answers3

1

I would recommend to write

const contents = {person: {...}, info: {...}}
cosnt promiseArray = [];
for (const key in contents) {
  let [method, url, hash] = getters.api_props(key);
  if (method) {
    promiseArray.push(api[method](url, hash)).then(value => ({
      key,
      status: 'fulfilled',
      value
    }), reason => ({
      key,
      status: 'rejected',
      reason
    })))
  }
}

const results = await Promise.all(promiseArray);
for (const r of results) {
  if (r.status=='fulfilled') {
    console.info(r.key, ':', r.value.data)
    commit("save", [r.key, r.value]);
  } else if (r.status=='rejected') {
    console.warn(r.key, ':', r.reason)
  }
})
commit("backup");
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Is there a reason to prefer Promise.all over Promise.allSettled? – g_ap Nov 06 '22 at 18:32
  • @g_ap Since the promises in the `promisesArray` are never rejected anyway, I thought this was a little more elegant. – Bergi Nov 06 '22 at 19:15
0

So, to answer my own question, after Bergi's comments I filled promiseArray with

api[method](url, hash).then((r) => [key, r]).catch((e) => {throw [key, e.response]})

and then found the key that I needed:

await Promise.allSettled(promiseArray).then((results) => {
    results.map((r) => {
      if (r.status=='fulfilled') {
        console.info(r.value[0],':',r.value[1].data)
      }
      if (r.status=='rejected') {
        console.warn(r.reason[0],':',r.reason[1])
      }
    })
  })
g_ap
  • 63
  • 1
  • 7
-1

You obviously don't need to take this, but I fiddled with it for a while and this is what I liked best:

import forEach from 'lodash/forEach'
import mapValues from 'lodash/mapValues'
import { api, getters } from 'somewhere'

var contents = {person: {...}, info: {...}}

const promiseContents = mapValues(contents, (value, key) => {
    let [method, url, hash] = getters.api_props(key);

    if (!method) { return }

    return api[method](url, hash)
})

await Promise.allSettled(Object.values(promiseContents))

forEach(promiseContents, (promise, key) => {
    promise.then(response => {
        if (promise.status === 'rejected') {
            console.warn(key, ':', response)
        }

        console.info(key, ':', value.data)
    })
})

The big requirement is that you include lodash in the project, but that is not an unusual ask in javascript projects.

mapValues allows you to keep the structure of your contents object while replacing the values with promises. I just use await on Promise.allSettled to tell the rest of the code when it can go. I just ignore the results.

Finally, using lodash's forEach I interpret the results. The advantage here is that every promise is run in a function alongside the key from your original contents object.

I like doing it this way because it doesn't require you to create a [key, result] array. That said, either way works fine.

Potter
  • 633
  • 7
  • 13
  • What is the point of using `await Promise.allSettled` if afterwards you still have to call `promise.then`? There's no synchronous access to the results. – Bergi Nov 06 '22 at 18:13
  • The original task statement said they needed to "make some API requests simultaneously, according to some contents and then process the results" this sounds like processing the results needs to happen roughly at the same time. If that is not a requirement, then the `Promise.allSettled` is not required as you say. – Potter Nov 06 '22 at 18:17