I am trying to reduce
an array of both asynchronous and synchronous methods. I have two synchronous methods and one asynchronous methods that does some basic formatting an object. Since I have a mix of asynchronous and synchronous methods, I am using async/await
with my reduce
. The problem is my formatName
method returns the error Cannot read property 'toUpperCase' of undefined
because the person
parameter passed in is a promise. I thought that since i use await
in my reduce, the callback should return the actual value instead of a promise:
const results = await fn(finalResult);
My code works fine if i take out the synchronous methods from my array. But I need to reduce an array of both synchronous and asynchronous methods. Does anyone have tips on how I can get around this? My full code is below.
const formatName = (person) => {
person.name = person.name.toUpperCase();
return person;
}
const formatAge = (person) => {
person.age = 10;
return person;
}
// Async function
const formatLocation = async (person) => {
await new Promise(resolve => setTimeout(resolve, 1000));
person.location = `${person.location.toLocaleLowerCase()}`
return person;
}
const initialPerson = {
name: "john",
age: 35,
location: 'USA'
}
const formattedStagesWithAsync = [
formatLocation, // asynchronous
formatAge, // synchronous
formatName //synchronous
];
const process = async () => {
const formattedPerson = await formattedStagesWithAsync.reduce(async (finalResult, fn) => {
const results = await fn(finalResult);
return results;
}, initialPerson);
console.log(`Formatted person - ${JSON.stringify(formattedPerson, null, 2)}`);
}
process();
CodePudding user response:
Async functions always return promises, so on subsequent iterations of the .reduce
callback, the accumulator will be a Promise that you need to wait to resolve first.
While you could do
const accumVal = await finalResult
at the beginning of the callback, this would be a lot simpler by avoiding reduce
entirely IMO.
const process = async () => {
let person = initialPerson;
for (const fn of formattedStagesWithAsync) {
person = await fn(person);
}
In the case that each of your functions returns the original object, like in the case here, that simplifies to:
const process = async () => {
for (const fn of formattedStagesWithAsync) {
await fn(initialPerson);
}
(you're mutating the initialPerson
in your original code too)
CodePudding user response:
Promise
return async
value but dont use async
and await
option for Promise
, Just return
it
const promiseHandler = () => {
return new Promise((done, reject) => {
setTimeout(() => {
done("hello");
});
});
}
promiseHandler().then(v => {
console.log(v); // hello
});