Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript, how to await multiple promises [duplicate]

What I want to accomplish:

  • gather artist id`s
    • either finding them in the db
    • or creating them
  • creating an event in the db, getting the event_id
  • waiting till both are done, artists and event id`s gathered
  • now looping over the artist, event combinations

What I got:

I`m working with Node and mysql. To insert the relations I have to wait for the artists to insert or create. I try to accomplish with the following code:

let promises = [];

if (artists.length != 0) {
    for (key in artists) {
        promises.push( find_artist_id_or_create_new_artist(artists[key]) )
    }
}

await Promise.all(promises);

Returning an id:

async function find_artist_id_or_create_new_artist(artist_name) {
    return await find_artist_return_id(artist_name, create_artist_return_id)
} 

Finding an artist:

async function find_artist_return_id(artist_name, callback) {
    var sql = "SELECT * FROM `artists` WHERE `name` LIKE "+con.escape(artist_name)+" LIMIT 1;"

    con.query(sql, (err,row) => {
      if(err) throw err;

      if (row.length == 0) {
        return callback(artist_name)
      } else {
        return row[0].id
      }
    });
}

Creating an artist

async function create_artist_return_id(artist_name) {
    var sql = "INSERT INTO `artists` (`id`, `name`, `meta_1`, `meta_2`) VALUES (NULL, "+con.escape(artist_name)+", NULL, NULL)";

    con.query(sql, (err, result) => {
      if(err) throw err;

      return result.insertId
    });
}

I understand that I cant return in a con.query function, but I dont how to properly setup the code to get this done. A link to, or help how to search for an answer is appreciated.

like image 744
bart great Avatar asked Apr 03 '18 09:04

bart great


People also ask

Can you await the same promise multiple times?

No. It is not safe to resolve/reject promise multiple times. It is basically a bug, that is hard to catch, becasue it can be not always reproducible.

How do you handle multiple promises?

In this approach, we will use Promise. all() method which takes all promises in a single array as its input. As a result, this method executes all the promises in itself and returns a new single promise in which the values of all the other promises are combined together.

How do you do multiple promises in parallel?

So, to run all these APIs in parallel, we can use Promise. all() like so. As you can tell, now we're running all the three APIs in parallel through Promise. all() through a single await .

Does promise allSettled run in parallel?

allSettled(promises) is a helper function that runs promises in parallel and aggregates the settled statuses (either fulfilled or rejected) into a result array.


2 Answers

Your fundamental SQL functions need to be converted to promises in order to be awaited.

See Async Function, Promise and Array.prototype.map() for more info.

// Artist Ids.
const artistIds = await Promise.all(artists.map(async (artist) => await findArtist(artist) || await createArtist(artist)))

// Find Artist.
const findArtist = artist => new Promise((resolve, reject) => con.query(`SELECT * FROM \`artists\` WHERE \`name\` LIKE ${con.escape(artist)} LIMIT 1;`, async (error, row) => {
  if(error) return reject(error)
  if (!row.length) return resolve(await createArtist(artist)) 
  return resolve(row[0].id)
}))

// Create Artist.
const createArtist = artist => new Promise((resolve, reject) => con.query(`INSERT INTO \`artists\` (\`id\`, \`name\`, \`meta_1\`, \`meta_2\`) VALUES (NULL, ${con.escape(artist)}, NULL, NULL)`, (error, result) => {
  if (error) return reject(error)
  return resolve(result.insertId)
}))
like image 76
Arman Charan Avatar answered Oct 15 '22 10:10

Arman Charan


You just need to wrap the mysql callbacks into promises:

 function find_artist_return_id(artist_name) {
  return new Promise((resolve, reject) => {
     var sql = "SELECT * FROM `artists` WHERE `name` LIKE "+con.escape(artist_name)+" LIMIT 1;"

      con.query(sql, (err,row) => {
         if(err) return reject(err);

         if (row.length == 0) {
           return resolve(artist_name);

           return resolve(row[0].id);      
      });
   });
}

And by the way, this is very ugly:

 if (artists.length != 0) {
   for (key in artists) {

Just do:

  for(const artist of artists)
    promises.push(someApiCall(artist));

or:

  const promises = artists.map(someApiCall);
like image 28
Jonas Wilms Avatar answered Oct 15 '22 09:10

Jonas Wilms