提问者:小点点

Javascript,如何等待多个promise[重复]


我想完成的是:

  • 收集艺术家ID`S
    • 在数据库中查找它们
    • 或创建它们

    我得到的是:

    我正在处理Node和MySQL。要插入关系,我必须等待艺术家插入或创造。我尝试使用以下代码来完成:

    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);
    

    返回ID:

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

    寻找艺术家:

    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
          }
        });
    }
    

    创造一个艺术家

    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
        });
    }
    

    我知道我不能在con.query函数中返回,但是我不知道如何正确地设置代码来完成这个任务。一个链接,或帮助如何搜索一个答案是感激的。


共3个答案

匿名用户

您的基本SQL函数需要转换为承诺,才能成为等待

有关更多信息,请参见Async FunctionPromiseArray.Prototype.Map()

// 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)
}))

匿名用户

您只需将mysql回调包装成承诺:

 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);      
      });
   });
}

顺便说一句,这很难看:

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

只要做:

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

或者:

  const promises = artists.map(someApiCall);

匿名用户

简单的方法是使用已经存在的ORM,如sequalizejs或等,它们为您返回promise,而不是在本地MySQL驱动程序中分别在原始查询中运行find和create。你可以简单地使用API,比如查找或创建一些东西。

我在您的示例中解释了async是如何工作的,我从您的示例中获取了一段代码。

    async function createArtist(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
    });
}

const artistId = (async () => {
    await createArtist('maxi');
})();

查看createArtist函数这正是您所拥有的。这里有三件事你必须注意,

  1. 您将它声明为异步函数,以便它在默认情况下返回promise.
  2. 它可以简单地返回任何类似于普通函数的内容,如果它是同步函数,则避免使用异步。
  3. 正如您所说的,您不能从回调中返回任何内容。因为它是异步的,所以必须返回promise.

因此可以将代码更改为

    async function createArtist(artist_name) {
    return new Promise((resolve,reject)=>{
        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) reject(err);
            resolve(result.insertId)
        });
    });
}

const artistId = (async () => {
    await createArtist('maxi');
})();

这里做了一些改变,添加了原生的promise包装,并在它变得异步之前返回它。叫做决心取得成功。拒绝让它失败。

别忘了添加try.。。catch blog来处理错误。