JavaScript array .reduce with async/await
The problem is that your accumulator values are promises - they're return values of async function
s. To get sequential evaluation (and all but the last iteration to be awaited at all), you need to use
const data = await array.reduce(async (accumP, current, index) => {
const accum = await accumP;
…
}, Promise.resolve(…));
That said, for async
/await
I would in general recommend to use plain loops instead of array iteration methods, they're more performant and often simpler. Array reduce function with async await
In your first try, result
is a promise as all async
functions evaluate to a promise when called, so you have to await result
before you can push to the array, and then you don't need the Promise.all:
newObjectArray = await objectAray.reduce(async (result, el) => {
const asyncResult = await someAsyncTask(el);
if (asyncResult) {
(await result).push(newSavedFile);
}
return result;
}, []);
But I'd guess that it is way faster to just filter afterwards: newObjectArray = (await Promise.all(objArray.map(someAsyncTask))).filter(el => el);
Typescript array reduce with async/await showing type mismatch error
Since the callback is async, it'll always return a Promise; the type parameter should not be Person[]
, but Promise<Person[]>
. You also need to pass such a value as the initial value, and wait for the prior accumulator to resolve before concating.
const ageByPersonPromise = persons.reduce<Promise<Person[]>>(
async (resultPromise, person) => {
const result = await resultPromise;
await sleep(4000);
return result.concat({
age: person.age,
name: person.name,
});
},
Promise.resolve([])
);
ageByPersonPromise
.then((ageByPerson) => {
console.log('age ', ageByPerson);
});
Reduce with async logic
Given that your reduce callback is async, the returned result is a promise. Notice how your log of result
is a Promise. To successfully access the value from the previous iteration in the current, you must await
that value first.
So instead of
result.readyScoreRequests.push(scoreInfo)
You should await the result to get the inner value.const resultRes = await result
resultRes.readyScoreRequests.push(scoreInfo)
Here is a simplified example of this that should help to clarify:function multiply(value){
return new Promise(res => {
setTimeout(() => {
res(value*2)
}, 100)
})
}
async function runSum(values){
const sum = await values.reduce(async (acc, cur) => {
const value = await multiply(cur)
const accRes = await acc
return accRes + value
}, 0)
console.log(sum)
}
runSum([1,2,3,4])
initialization in async reduce
An async
function returns Promise, which makes it difficult to work with when using .reduce()
as you would need to await
your accumulator each iteration to get your data. As an alternative, you can create an array of Promises using the mapper function of Array.from()
(which you can think of as using .map()
directly after Array.from()
). The idea here is that the map will trigger multiple asynchronous calls for each file by using sendImage
/sendFile
. These calls will run in parallel in the background. The value that we return from the mapping function will be a Promise that notifies us when the asynchronous call has successfully completed (once it resolves). Moreover, the mapping function defines what the promise resolves with, in our case that is the new object with the src
property:
const isImage = file => file.type.startsWith('image/');
const filePromises = Array.from(target, async file => {
const response = await (isImage(file) ? channel.sendImage(file) : channel. sendFile(file));
return {...file, type: file.type, src: response.file};
});
Above filePromises
is an array of Promises (as the async
mapper function returns a Promise implicitly). We can use Promise.all()
to wait for all of our Promises to resolve. This is faster than performing each asynchronous call one by one and only moving to the next once we've waited for the previous to complete:setIsLoading(true); // set loading to `true` before we start waiting for our asynchronous work to complete
const fileObjects = await Promise.all(filePromises);
setIsLoading(false); // complete asynchronous loading/waiting
Lastly, fileObjects
is an array that contains all objects, both files and images. We can do one iteration to partition this array into seperate arrays, one for images, and one for files:const attachments = {files: [], images: []};
for(const fileObj of fileObjects) {
if(isImage(fileObj))
attachments.images.push(fileObj);
else
attachments.files.push(fileObj);
}
using async await with reduce
The problem is that acc
on the second iteration is a promise object - that's why you await
it. However, you still assign the [cur]
property on that promise object, not on the promise result, and the implicit promise chaining of the async
function as well as the explicit await
s will just ignore properties on the promise object. You could fix this by doing acc = await acc;
, but really I recommend not to use reduce
with async
/await
at all. A normal loop is much simpler and has no pitfalls.
const imageField = ["logoUrl", "fullLogoUrl"]
const onCreate = async (submitData: any) => {
const uploadImageField = {};
for (const cur of imageField) {
const url = await uploadImage(submitData.general[cur][0]);
acc[cur] = url;
}
console.log(uploadImageField);
}
Why reduce with async function returns only the last object
You're very close. The main issue is that you wrote the array as [(...)]
instead of [...]
. In your code, the array only contains one value, if you remove the ()
you will have three values. See comma operator for details.
The other thing you have to consider is that an async
function always returns a promise, so you must await o
as well -
(async () =>
console.log(
await ["apple", "banana", "orange"].reduce(async (o, fruit) => {
const rand = new Promise((resolve) => setTimeout(() => resolve(Math.random()), 200))
return { ...await o, [fruit]: await rand }
}, {})
))();
Related Topics
Why Do I Need to Await an Async Function When It Is Not Supposedly Returning a Promise
Detect Whether Scroll Event Was Created by User
Remove HTML Comments with Regex, in JavaScript
JavaScript Roundoff Number to Nearest 0.5
How to Check If an Array Is a Subset of Another Array in JavaScript
JavaScript - Generating All Combinations of Elements in a Single Array (In Pairs)
Regular Expression for Ip Address Validation
Ecmascript 2015: Const in for Loops
Set Window to Fullscreen (Real Fullscreen; F11 Functionality) by JavaScript
Click Everywhere But Here Event
How to Load a JSON Object from a File with Ajax
Why Does JavaScript Getyear() Return 108
Printing a Web Page Using Just Url and Without Opening New Window
React Jsx: Selecting "Selected" on Selected <Select> Option
How to Pass Data from Child to Parent Component Angular
Angular Ui-Router $Urlrouterprovider .When Not Working When I Click <A Ui-Sref="...">