I'm working on a function that uses Array.reduce, and I need to add an asynchronous API call inside the reduce function. This requires me to use an async function for the callback I pass into reduce
, since I'm using await
inside the function to wait for the asynchronous API call.
I'm having some trouble writing the reduce correctly. Here's how it currently is (working):
const result = records.reduce((array, currValue) => {
//do stuff
return array
}, [])
Here's what I tried to change it to:
const result = records.reduce(async(array, currentValue) => {
// do stuff
someValue = await asyncCall(currentValue)
array.add(someValue)
return array
}, [])
The error I'm getting is 'No overload matches this call'.
This seems to make sense to me, since reduce takes in a callback that returns an array, and async functions return a callback, not an array. But when I read other examples of how to pass async functions into .reduce, they all seem to just pass an async function into reduce with no problem.
Here are a few links I looked at:
/
JavaScript array .reduce with async/await
/
The moment I declare the reduction function into async, I get the no matching overloads error, which makes sense to me. I'm not sure how this seems to work for other people.
I'm working on a function that uses Array.reduce, and I need to add an asynchronous API call inside the reduce function. This requires me to use an async function for the callback I pass into reduce
, since I'm using await
inside the function to wait for the asynchronous API call.
I'm having some trouble writing the reduce correctly. Here's how it currently is (working):
const result = records.reduce((array, currValue) => {
//do stuff
return array
}, [])
Here's what I tried to change it to:
const result = records.reduce(async(array, currentValue) => {
// do stuff
someValue = await asyncCall(currentValue)
array.add(someValue)
return array
}, [])
The error I'm getting is 'No overload matches this call'.
This seems to make sense to me, since reduce takes in a callback that returns an array, and async functions return a callback, not an array. But when I read other examples of how to pass async functions into .reduce, they all seem to just pass an async function into reduce with no problem.
Here are a few links I looked at:
https://advancedweb.hu/how-to-use-async-functions-with-array-reduce-in-javascript/
JavaScript array .reduce with async/await
https://gyandeeps./array-reduce-async-await/
The moment I declare the reduction function into async, I get the no matching overloads error, which makes sense to me. I'm not sure how this seems to work for other people.
Share Improve this question asked Apr 23, 2020 at 16:33 Bob DoleBob Dole 3651 gold badge4 silver badges11 bronze badges 1-
1
The first article you linked includes a key difference: your
.reduce()
call will return a Promise (not a callback), so you need to gather the actual result in a.then()
callback or else place that call to.reduce()
inside a wrapperasync
function so that it too can be used withawait
. – Pointy Commented Apr 23, 2020 at 16:38
2 Answers
Reset to default 7First: reduce
probably isn't the best tool to use for this. It looks like you're just adding entries to an array. reduce
is overplicated for that task, particularly if you're doing something asynchronous. Instead, a looping construct that you can use in an async
function is much, much simpler.
I'll start with reduce
, then go to the looping construct.
reduce
works synchronously. If you pass an async
function in as its callback, the promise that function returns will be the accumulator value seen by the next callback. So if one of the steps in the reduce operation needs to be asynchronous and return a promise, every step after it has to be asynchronous returning a promise (for simplicity, it's best to just make every step return a promise); and the result of the reduce
will be a promise for the eventual final value, not the final value itself. You can't make an asynchronous call synchronous, and you can't make a synchronous operation (reduce
) wait for an asynchronous result.
So, all of your callbacks will be dealing with promises. It'll look a bit like this:
const result = await records.reduce(async(arrayPromise, currentValue) => {
// −−−−−−−−−−−−^^^^^−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^
const array = await arrayPromise // <=====
// do stuff
someValue = await asyncCall(currentValue)
array.push(someValue) // <==== `push` rather than `add`, presumably
return array
}, Promise.resolve([]))
// ^^^^^^^^^^^^^^^^−−^
Of course, since that uses await
, it has to be in an async
function. Otherwise:
records.reduce(async(arrayPromise, currentValue) => {
const array = await arrayPromise // <=====
// do stuff
someValue = await asyncCall(currentValue)
array.push(someValue)
return array
}, Promise.resolve([]))
.then(result => {
// ...use `result` here
})
.catch(error => {
// ...handle/report error here...
})
You're better off with a looping construct that natively supports being part of an async
function:
const result = []
for (const currentValue of records) {
someValue = await asyncCall(currentValue)
result.push(someValue)
}
// ...use `result` here...
or even
const result = []
for (const currentValue of records) {
result.push(await asyncCall(currentValue))
}
// ...use `result` here...
If you need to do this in a function that isn't an async
function, you'll be dealing explicitly with a promise, which would look like:
(async () => {
const result = []
for (const currentValue of records) {
result.push(await asyncCall(currentValue))
}
return result
})()
.then(result => {
// ...use `result` here
})
.catch(error => {
// ...handle/report error here...
})
I think the simplest thing would be as following
const promises = records.reduce((array, currentValue) => {
// do stuff
someValue = asyncCall(currentValue)
array.add(someValue)
return array
}, [])
const results= Promise.all(promises);
If the use-case for your reduce function is more plicated, please post more code or create a sandbox