Reputation: 183
I have a function which will take am array of intergers, then it will call an other function which will take one interger and do some check and return a promise to resolve it.
I know that getDataById function is not relly doing async job, but keep in my this is just examples..
This is my code:
function getDataById(id) {
return new Promise((resolve, reject) => {
if (id > 0) resolve(id * 10);
if (id <= 0) reject("wrong input");
})
}
async function callService(idList) {
return await idList.map((id) => getDataById(id));
}
let teamIDList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, -1, -2, -3, -4];
callService(teamIDList).then(res => {
console.log(res);
});
I expect it will return an array of new numbers and some strings inside. But it returned an array of promises.
Upvotes: 0
Views: 3209
Reputation: 1074355
await
doesn't do anything special when you give it an array, it just wraps the array in a resolved promise.
To wait for the promises to settle, use Promise.all
:
async function callService(idList) {
return await Promise.all(idList.map((id) => getDataById(id)));
}
Alternately:
function callService(idList) {
return Promise.all(idList.map((id) => getDataById(id)));
}
(Side note: No need for that arrow function:
return await Promise.all(idList.map(getDataById));
)
Note that both of those do things in this order:
getDataById
for each id
callService
's promise (remember, async
functions always return promises)If you wanted to wait for each call to getDataById
to complete before doing the next call to getDataById
, you'd do it with a simple loop:
// **IF** you wanted to wait for each `getDataById` call to finish
// before starting the next
async function callService(idList) {
const result = [];
for (const id of idList) {
result.push(await getDataById(id));
}
return result;
}
(You can also shoehorn that into a reduce
if you want, but it doesn't buy you anything.)
But I think you want the first code block above, starting all the calls in parallel and waiting for them to finish.
You've said in a comment that your boss doesn't want you to use Promise.all
because it rejects if any of the promises rejects. That's true, it does. If you don't want that, you may want to use allSettled
instead. That's a Stage 4 proposal (e.g., actively being implemented in JavaScript engines) which is easily polyfilled.
async function callService(idList) {
return await Promise.allSettled(idList.map(getDataById));
}
Alternately:
function callService(idList) {
return Promise.allSettled(idList.map(getDataById));
}
The result is an array of objects where the fulfilled promises look like this:
{
status: "fulfilled",
value: (the value)
}
and the rejected ones look like this:
{
status: "fulfilled",
reason: (the rejection reason)
}
Obviously, if you want to transform that array, you can easily do so. For instance, if you wanted to return null
for the ones you couldn't retrieve:
async function callService(idList) {
return (await Promise.allSettled(idList.map(getDataById)))
.map(entry => entry.status === "fulfilled" ? entry.value : null);
}
Or if you wanted to filter them out:
async function callService(idList) {
return (await Promise.allSettled(idList.map(getDataById)))
.map(entry => entry.status === "fulfilled" ? entry.value : null)
.filter(entry => entry !== null);
}
If you want to filter and map simultaneously, we can slightly abuse flatMap
: :-)
async function callService(idList) {
return (await Promise.allSettled(idList.map(getDataById)))
.flatMap(entry => entry.status === "fulfilled" ? entry.value : []);
}
Upvotes: 3