简体   繁体   中英

Cannot figure out how to wait for Promise

I have an array with user IDs and I need to find out what name belongs to each ID and return them in an array. I can get the user's name from the database, using knex and push them into an array, but when I try to send the data it is always an empty array.

I am not really good with Promises so can't figure out how to apply to my project.

const userId = [10,11,12,13]
let users = []

userId.map(id => {
    db.select('name').from('users').where('user_id', id)
    .then(user => {
        users.push(user)
    })
})
res.json(users)

I want the response to wait until the looping finishes and send the users array.

Your map is creating an array of undefined because your callback function doesn't return anything. If we tweak it slightly, it'll create an array of promises, which conveniently is exactly what Promise.all expects. :-) So:

const userId = [10,11,12,13]
Promise.all(
    userId.map(id => db.select('name').from('users').where('user_id', id))
)
.then(users => {    // `users` is an array of users, in the same order as the IDs
    res.json(users);
})
.catch(error => {
    // Render an error response
});

First you need to wait for all promises to finish before running res.json(...)

Second, you shouldn't mutate outside variables after promise resolving (the order by which the promises resolve will alter your output and that is not nice.

Something like this should work fine

const userId = [10,11,12,13]

// map userId array to promise array
// Promise.all aggregates a promise array into one big promise that resolves when all promises resolve (and preserves array order)
Promise.all(
  userId.map(id =>
    db
      .select("name")
      .from("users")
      .where("user_id", id)
  )
)
  .then(users => res.json(users))
  .catch(e => console.error("Error::", e));

/*handle error in the catch block*/

/* visual example of Promise.all.then block
Promise.all([           users = [
   getUser(10),    ->     {userId: 10, ....}
   getUser(11),    ->     {userId: 11, ....}
   getUser(12)     ->     {userId: 12, ....}
])                      ]

*/

As an alternative answer, here's how you could make 1 trip to the DB for this particular query meaning you don't need to wait for multiple Promises and reduce the load on your DB

knex.raw(
  'select name from users where user_id in (' + userId.map(_ => '?').join(',') + ')', 
  [...userId]
);

You want Promise.all() , see here .

Try

const userId = [10,11,12,13]

let users = userId.map(id => new Promise(resolve => {
    db.select('name').from('users').where('user_id', id)
    .then(user => {
        resolve(user)
    })
}))
Promise.all(users).then(()=>res.json(users))

Here users is an array of promises. As soon as all of them are resolved, do res.json(users) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM