Home > Software engineering >  How to pass variable from each promise to a Promise.allSettled?
How to pass variable from each promise to a Promise.allSettled?

Time:11-07

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?

CodePudding user response:

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])
      }
    })
  })

CodePudding user response:

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");

CodePudding user response:

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.

  • Related