Search code examples
javascriptpromiseflatmappromise.all

What is the proper way to write conditional statement inside flatMap with Promise.all in Javascript


I am working on a flatMap with Promise.all; there are two condition inside flatMap, the originalObj is for checking whether originalObj.state is false or not, then implement the insertRealStartTime, and in every event, I will implement the Event.updateMany regardless the condition, and I want to return it as well because the return of Events.updateMany() told me whether or not the update is a success.

function async foo(){
  const eventPromises = events.flatMap(async (event) => {
      if (event.state.matchPhase == "1H") {
        // simpleQuery is async function
        const originalObj = await simpleQuery({ id: event.id });
        if (originalObj.state == false) {
          // insertRealStartTime is async function
          await insertRealStartTime(event.id);
        }
      }
      // Event.updateMany is async function
      await Events.updateMany(
        { ...event, expireAt: event["startTime"] },
      );
    }
  );
  const all = await Promise.all(eventPromises);
  console.log(all)
}

I got an array of undefined in console.log(all); I think I probably shouldn't use await inside flatMap because it lost the meaning of using Promise.all; however, how should I deal with all these if-statement to make sure the async function will execute base on the if-statement with Promise.all?


Solution

  • Your flatMap callback does not have a return statement, so the promises returned by those async calls will resolve to undefined.

    As you want the get the values that the updateMany() promises resolve to, you should return those.

    There are at least two ways to do that:

    1. Get the value that await Events.updateMany() evaluates to, and then return it (You would of course add the argument):

      const success = await Events.updateMany();
      return success;
      
    2. Return the promise returned by Events.updateMany():

      return Events.updateMany();
      

    Either works.

    Not a problem, but if the callback returns a promise (as is your case), there is no reason to use flatMap. flatMap would be useful if you expect arrays as return values for the callback, but that is impossible with async callbacks -- they always return promise objects. So you can just use map.

    With some other updates to your code, you get this:

    function async foo() {
      const eventPromises = events.map(async (event) => {
          if (event.state.matchPhase == "1H") {
            // You can use destructuring here, as you're only interested in the state property
            const {state} = await simpleQuery({ id: event.id });
            if (!state) { // Use NOT operator
              await insertRealStartTime(event.id);
            }
          }
          // Return the promise
          return Events.updateMany(
            { ...event, expireAt: event.startTime }
          );
        }
      );
      const all = await Promise.all(eventPromises);
      console.log(all);
      return all; // Provide the caller something to work with...
    }