1

I am wondering how to assign values to multiple different keys asynchronously with Await/Async.

Initially I had imagined merely calling the Async function would assign the values such as below:

const getMetrics =  async (metric, key) => {
  const rawResponse = await fetch(`http:/localhost:8080/${metric}`, 
 {
    method: 'POST'
    body: JSON.stringify({
    projects:[
      {
        name: key
      }]
  }
)
  });

  return await rawResponse.json();
};

  const metrics =  {
    metric1:  {
      key1: getMetrics("metric1", "key1"),
      key2: getMetrics("metric1", "key2"),
      key3: getMetrics("metric1", "key3")
    },

    metric2: {
      key1: getMetrics("metric2", "key1"),
      key2: getMetrics("metric2", "key2"),
      key3: getMetrics("metric2", "key3")
    }
}

Obviously this isn't how async works since await must be called to actually retrieve the values from the resolved promise. But it did asynchronously retrieve and assign the values to the key (albeit promises instead of their values) which is sort of what I was looking for.

So the question is how do I actually get the values assigned to the keys asynchronously?

  const metrics =  {
    metric1:  {
      key1: await getMetrics("metric1", "key1"),
      key2: await getMetrics("metric1", "key2"),
      key3: await getMetrics("metric1", "key3")
    },

    metric2: {
      key1: await getMetrics("metric2", "key1"),
      key2: await getMetrics("metric2", "key2"),
      key3: await getMetrics("metric2", "key3")
    }
}

I tried this and it did assign the values but clearly it is completely counterintuitive since it's synchronous sequential at that point.

Typically for just standard variables assigned to async functions, I would just add the function calls to an array and destructure the functions into variables using

const asyncFunctions = [val1(), val2(), val3()];
const [key1,key2,key3] = await Promise.all(asyncFunctions);

How do I accomplish something similar for objects?

Saga
  • 53
  • 6
  • Does `getMetrics` really return the desired values in the right order, without any parameters? (or is that just an example whose simplification we can ignore) – CertainPerformance Mar 04 '19 at 10:46
  • @CertainPerformance Yes sorry it is just an example, I had to take out certain code since it was relevant to work. I'll change it a bit. The values to be returned are a JSON object from an API (localhost in this case) – Saga Mar 04 '19 at 10:49
  • metric1 = {} metric1.key1 = await getMetrics() you can do this way however you will have to return the object inside a promise i.e. inside an async function – AZ_ Mar 04 '19 at 10:51
  • 2
    Note that `return await` is an anti-pattern. Just return the promise. – Amadan Mar 04 '19 at 10:54
  • I don't understand what you mean by "*it is completely counterintuitive since it's synchronous at that point.*". Do you mean [sequential](https://stackoverflow.com/questions/44037598/async-await-assignment-to-object-keys-is-it-concurrent/)? – Bergi Mar 04 '19 at 12:19
  • @Bergi Sorry, you are completely right about the grammar. Also I disagree, my question is about async/await primarily and not just promises alone like the question you linked – Saga Mar 04 '19 at 13:03
  • @Saga `async`/`await` does have no tools for concurrency, it always only awaits a single promise. Or maybe I don't get what you are asking for? The answers below seem be the same as in the suggested duplicate. – Bergi Mar 04 '19 at 13:30

4 Answers4

3

Use a similar method to your current one of destructuring an array from Promise.all, then reform the values into an object:

const promises = [getMetrics(), getMetrics(), getMetrics(), getMetrics(), getMetrics(), getMetrics()];
const [key1_1,key1_2,key1_3, key2_1, key2_2, key2_3] = await Promise.all(promises);
const metrics = {
  metric1: {
    key1: key1_1,
    key2: key1_2,
    key3: key1_3
  },
  metric1: {
    key1: key2_1,
    key2: key2_2,
    key3: key2_3,
  }
};
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
2

There are libraries that have functions like Promise.all but which work with object properties instead of with array elements. For example, Bluebird's Promise.props:

const metrics = await Promise.props({
  metric1: Promise.props({
    key1: getMetrics(),
    key2: getMetrics(),
    key3: getMetrics()
  }),

  metric2: Promise.props({
    key1: getMetrics(),
    key2: getMetrics(),
    key3: getMetrics()
  })
})

It's reasonably easy to implement, if you don't need all the bells and whistles; basically,

if (!Promise.props) {
  Promise.props = async object => {
    let keys = Object.keys(object);
    let values = await Promise.all(Object.values(object));
    let result = {};
    for (let i = 0; i < keys.length; i++) {
      result[keys[i]] = values[i];
    }
    return result;
  }
}
Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Thanks a lot. It's too bad promise.all doesn't work on objects (yet?) – Saga Mar 04 '19 at 11:10
  • Yeah. But I could have sworn I worked with Promise implementation that did... Still, as I show, the very basic version is quite easy to code up, if you aren't using any Promise library that doesn't have it already. – Amadan Mar 04 '19 at 11:17
0

How do I accomplish something similar for objects?

You use Promise.all to get an array of results, and then copy the values out of the array and into the object.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
0

I think async function return a promise not a value. Andawait must inside async function. And you can only feel like sync inside async function.

async function getVal(key){
    return await Promise.resolve(key)
}
let keys={}
async function getKeys(){
    keys={
        key1:await getVal("key1"),
        key2:await getVal("key2"),
        key3:await getVal("key3")
    }
    console.log(keys)
}
getKeys()
Nana Sun
  • 91
  • 3