DEV Community

Yonz
Yonz

Posted on

Async Promise -> [Promises]

The post is about spawning promises from the result of a promise. I found myself in a weird patter when trying to fetch tweets for a list of twitter handles.

Bad Code

      if (args.handles?.length || args.handles?.length) {
        const tweetsByUsers = await fetchUsers({
          twitterAPI,
          ids: args.userIds,
          handles: args.handles
        }).then((users) => {
          if (!users) return [];
          promises.push(
            ...Object.values(users).map((user) => {
              fetchTweets(twitterAPI, user).then((tweetsByUser) => {
                tweets.push(...tweetsByUser);
                return tweetsByUser;
              });
            })
          );
        });
        promises.push(tweetsByUsers);
      }
...
      await Promise.all(promises).catch((error) => {
        console.warn("Error getting Tweets: ", error);
      });
      return tweets;

Enter fullscreen mode Exit fullscreen mode

Working Code

Eventually I cleaned it up with:

      const promises: any = [];
      if (args.handles?.length || args.handles?.length) {
        const twitterUsers = await fetchUsers({
          twitterAPI,
          ids: args.userIds,
          handles: args.handles
        });
        if (twitterUsers) {
          for (const user of Object.values(twitterUsers!)) {
            const fetchTweetPromise = fetchTweets(twitterAPI, user).then((tweetsByUser) => {
              tweets.push(...tweetsByUser);
              return tweetsByUser;
            });
            promises.push(fetchTweetPromise);
          }
        }
      }
...
      await Promise.all(promises).catch((error) => {
        console.warn("Error getting Tweets: ", error);
      });
      return tweets;
Enter fullscreen mode Exit fullscreen mode

Takeaway 1: Pomise.all([promise1->[promise1a,promise1b]] does not wait for promise 1a and 1b.
Takeaway 1b: creatively trying to add promise1a and promise1b into top level fails. This is caused by a timing issue. Promise.all() will be waiting on promises before promise1a and promise1b are added to promises.

Takeaway 2 : .then( a => return new Promose()) creates havoc on its own but .then([users] => return users.map(user-> new Promise(user)) is a mind bender.

Top comments (2)

Collapse
 
yonz profile image
Yonz • Edited

Asked you.com to fix the bad code and got the response.
Prompt: Fix the issue with the code:

The issue was with the promises.push() call which was pushing the promise instead of waiting for the promise to resolve. The correct syntax is to use Promise.all() to wait for all of the promises to resolve before returning the results.

To make sure that the tweetsByUsers variable is populated before returning, you need to use the await keyword before the Promise.all() call. This will cause the code to wait for all of the promises to resolve before returning the tweetsByUsers variable. For example:

if (args.handles?.length || args.handles?.length) {
  const tweetsByUsers = await fetchUsers({
    twitterAPI,
    ids: args.userIds,
    handles: args.handles
  }).then((users) => {
    if (!users) return [];
    return await Promise.all(
      Object.values(users).map((user) => {
        return fetchTweets(twitterAPI, user).then((tweetsByUser) => {
          tweets.push(...tweetsByUser);
          return tweetsByUser;
        });
      })
    );
  });
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
yonz profile image
Yonz

I stubbornly wanted to solve the problem with .then( users -> forEach but alas I had to give up. Drop a line if you can solve it with that pattern.