skip to Main Content

I have an array.map function that simply iterate on an array and return array of promises. Now, I’m told to change it to .reduce instead of .map. However, when I implemented .reduce without changing any business logic, the time that method requires increased almost 10 times because it waits for the async calls for every element in the array.

Is there any way to use reduce to create an array of promises that will be resolved using Promise.all() later.

I simulated almost the same case and left some of the solutions I’ve tried and the desired case in the code below.

Can somebody show me how to do it?

interface IUser { name: string; balance: number | undefined; }

let user1 = { name: 'user1', balance: undefined }
let user2 = { name: 'user2', balance: undefined }
let user3 = { name: 'user3', balance: undefined }
const users = [user1, user2, user3]

async function calculateBalance(user: IUser): Promise<IUser> {
  await new Promise((resolve) => setTimeout(resolve, 5000));
  user.balance = Math.floor(Math.random() * 5000) + 1000;

  return user;
}

async function processUsers() {
  return users.reduce(async (acc, currUser, index) => {
    return [ ...acc, calculateBalance(currUser) ] // not working
    return acc.push(calculateBalance(currUser)) // Argument of type 'Promise<IUser>' is not assignable to parameter of type 'never'
    return acc.concat(calculateBalance(currUser)) // Same above
  }, [])
}

const processedUsersArray = await processUsers();
await Promise.all(processedUsersArray) // Desired thing for the sake of performance

2

Answers


  1. The push() method of Array instances adds the specified elements to the end of an array and returns the new length of the array.

    So in your reduce you are returning a number and try to push to it.

    You should be using map() not reduce and not mixing await with promise.

    const user1 = { name: 'user1', balance: undefined }
    const user2 = { name: 'user2', balance: undefined }
    const user3 = { name: 'user3', balance: undefined }
    
    const users = [user1, user2, user3]
    
    function calculateBalance(user) {
      return new Promise((resolve) => {
        setTimeout(() => {
          user.balance = Math.floor(Math.random() * 5000) + 1000;
          resolve(user);
        }, 1000)
      });
    }
    
    function processUsers(users) {
      return users.map(currUser => calculateBalance(currUser));
    }
    
    (async () => {
      const results = await Promise.all(processUsers(users));
      console.log(results);
    })();
    Login or Signup to reply.
  2. For the usecase you are showing a .map works perfectly fine

    interface IUser {
      name: string;
      balance: number | undefined;
    }
    const user1 = { name: 'user1', balance: undefined };
    const user2 = { name: 'user2', balance: undefined };
    const user3 = { name: 'user3', balance: undefined };
    const users = [user1, user2, user3];
    const init = async () => {
      async function calculateBalance(user: IUser): Promise<IUser> {
        await new Promise((resolve) => setTimeout(resolve, 5000));
        user.balance = Math.floor(Math.random() * 5000) + 1000;
    
        return user;
      }
    
      async function processUsers() {
        const promises = users.map((user) => calculateBalance(user));
        const processedUsersArray = await Promise.all(promises);
        return processedUsersArray;
      }
    
      const processedUsersArray = await processUsers();
      console.log(processedUsersArray);
    };
    
    init();
    

    However if you would need to use reduce you should be able to do it like this

    const promises = users.reduce((acc, user) => {
      acc.push(calculateBalance(user));
      return acc;
    }, [] as unknown as Promise<IUser>[]);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search