Reputation: 5648
Promise.all can turn [Promise a, Promise b]
into Promise [a, b]
, which is super useful, but is there also a way to turn {a: Promise a, b: Promise b}
into Promise {a, b}
.
The use case is:
I have a function that loads some files from a website and gives back error messages in the case that it failed.
This means, that its signature is information -> {values: values, messages: messages}
.
But the whole check is async, so it turns out to be information -> {values: Promise values, messages: promise messages}
Upvotes: 3
Views: 4930
Reputation: 11
With Object.fromEntries(iterable), since ES10 / ES2019, similiar to this post but added types:
async function promiseAllOfObject<T extends Object>(obj: T): Promise<{
[key in keyof T]: Awaited<T[key]>
}> {
const entries = Object.entries(obj).map(async ([key, value]) => [key, await value])
return Object.fromEntries(await Promise.all(entries))
}
Upvotes: 0
Reputation: 888
I would dare to suggest another option (with some further hints):
export async function promiseAllOfObject(obj) {
const values = await Promise.all(Object.values(obj));
return Object.keys(obj).reduce(
(res, key, index) => (res[key] = values[index], res),
{}
);
}
Respectively replacing Promise.all
to Promise.allSettled
will give you promiseAllOfObjectSettled()
function.
And, as a small bonus, here is typescript version (replacing to Promise.allSettled
works the same as above):
export async function promiseAllOfObject<
T extends Record<string, unknown> = Record<string, unknown>,
>(obj: {
[P in keyof T]: Promise<T[P]>;
}): Promise<T> {
const values = await Promise.all(Object.values(obj));
return Object.keys(obj).reduce((res, key, index) => {
res[key] = values[index];
return res;
}, {} as Record<string, unknown>) as T;
}
Upvotes: 0
Reputation: 260
Here's my super simple solution:
export const objectZip = (keys, values) =>
keys.reduce(
(others, key, index) => ({
...others,
[key]: values[index],
}),
{}
);
export const objectPromise = async obj =>
objectZip(Object.keys(obj), await Promise.all(Object.values(obj)));
Upvotes: 4
Reputation: 338178
You could do it manually:
function objectPromise(obj) {
return Promise.all(Object.keys(obj).map(function (key) {
return Promise.resolve(obj[key]).then(function (val) {
return {key: key, val: val};
});
})).then(function (items) {
var result = {};
items.forEach(function (item) {
result[item.key] = item.val;
});
return result;
});
}
usage
var testObj = {
a: Promise.resolve("a"),
b: Promise.resolve("b"),
c: "c"
};
objectPromise(testObj).then(function (obj) {
console.log(obj);
});
//> { a: 'a', b: 'b', c: 'c' }
Same thing in ES6 syntax, if you prefer that:
var objectPromise = obj => Promise.all(
Object
.keys(obj)
.map(key => Promise.resolve(obj[key]).then(val => ({key: key, val: val})))
).then(items => {
let result = {};
items.forEach(item => result[item.key] = item.val);
return result;
});
Upvotes: 2
Reputation: 425
No as i know but you can prepare a return object and you will know when its ready
function multipleAsyncJobs(){
ret = {};
var a = new Promise(function(resolve, reject){
ret.a = someVal; // set
resolve();
})
var b = new Promise(function(resolve, reject){
ret.b = someVal; // set
resolve();
})
return new Promise(function (resolve, reject) {
Promise.all([a,b]).then(function(){
resolve(ret)
})
})
}
u can use this func like this so that returns a promise as usual
multipleAsyncJobs().then(ret=>{
console.log(ret.a)
})
Upvotes: 0