Search code examples
javascriptarraysasynchronouspromisebluebird

How to Promise.all for nested arrays?


Structure of the data:

tasks: [
  {
    name: "get milk",
    users: ["abc", "def"]
  },
  {
    name: "buy bread",
    users: ["def", "ghi"]
  }
]

I need to get the email address of each one of the users from the database (so far so good) and wait for all tasks to be completed, then move on and do something with the data. The place where it doesn't work is written in the comments below:

var tasks_with_emails = tasks.map(function(task) {
  var emails = task.users.map(function(user) {
    return user_to_email(user); // from the database
  });
  Promise.all(emails).then(function(emails) {
    task.emails = emails;
    console.log(task); // this one works fine
    return task;
  }).catch(next);
});
Promise.all(tasks_with_emails).then(function(tasks) {
  console.log(tasks); // <==== this one fires too quickly
}).catch(next);

So tasks_with_email should wait for all nested emails to resolve first, but it doesn't.


Solution

  • Once you add the return in front of Promise.all(), your code works for me, as you can see in the below example. So have a look at what user_to_email() returns. This should be a promise that resolves into the email string.

    const user_to_email = user => new Promise(( resolve, reject ) => {
      setTimeout(() => resolve( `${ user }@example.com` ), 3000 );
    });
    const tasks  = [
      {
      name: "get milk",
      users: ["abc", "def"]
      },
      {
      name: "buy bread",
      users: ["def", "ghi"]
      }
    ];
    const tasks_with_emails = tasks.map( task => {
      const emails = task.users.map( user => {
        return user_to_email(user); // from the database
      });
      return Promise.all(emails).then( emails => {
        task.emails = emails;
        return task;
      });
    });
    Promise.all(tasks_with_emails).then( tasks => {
      console.log(tasks); // <==== this one fires too quickly
    });
    setTimeout(() => console.log( '1000ms' ), 1000 );
    setTimeout(() => console.log( '2000ms' ), 2000 );
    setTimeout(() => console.log( '2999ms' ), 2999 );