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

Posted

技术标签:

【中文标题】Javascript,如何等待多个承诺[重复]【英文标题】:Javascript, how to await multiple promises [duplicate] 【发布时间】:2018-09-12 14:25:32 【问题描述】:

我想要完成的事情:

收集艺术家ID 在数据库中找到它们 或创建它们 在数据库中创建事件,获取 event_id 等到两者都完成后,艺术家和活动 ID 已聚集 现在循环播放艺术家、事件组合

我得到了什么:

我正在使用 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 函数中返回,但我不知道如何正确设置代码来完成这项工作。感谢您提供指向或帮助如何搜索答案的链接。

【问题讨论】:

混合使用 mySql 回调和承诺将不起作用。一个简单的解决方案是使用 mySql 的包装器,该包装器已经将回调转换为 Promises.. 例如.. npmjs.com/package/promise-mysql 谢谢,你能推荐一个异步可以和不能工作的来源吗? 您的基本SQL functions 需要转换为promises 才能成为awaited await 可以等待任何 Promise,async 只是让函数返回 Promise 的一种方式,但如果在此函数内部使用回调,则您已经破坏了 Promise。所以在上面,如果你只使用 Promise 版本的 mySQL 你会这样做 -> var result = await con.query(sql) ,基本上不使用回调。 【参考方案1】:

您的基本SQL functions 需要转换为promises 才能成为awaited

请参阅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)
))

【讨论】:

不错的一个 :) 只是扩展解释,然后它的完美答案... 工作,为了让它工作这些是我必须编辑的东西:添加等待: const artistIds = await Promise.all(artists.map(async (artist) => await findArtist(artist) | | await createArtist(artist))) 添加参数: const createArtist = (artist_name) => new Promise((resolve, reject) => con.query(INSERT INTO `artists` (`id`, `name`, `meta_1`, `meta_2`) VALUES (NULL, $con.escape(artist_name), NULL, NULL), (error, result) => if (error) return reject(error) return resolve(result.insertId) )) 你能编辑这个然后我接受这个作为答案吗? 我的错啊。感谢您指出这些!很高兴它起作用了?@bartgreat【参考方案2】:

您只需要将 mysql 回调包装到 Promise 中:

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

【讨论】:

【参考方案3】:

简单的方法是使用已经存在的 ORM,例如 sequalizejs 等,它们会为您返回承诺,而不是在原生 MySQL 驱动程序中的单独原始查询中运行 find 和 create .您可以简单地使用 API,例如查找或创建内容。

我在你的例子中解释了异步是如何工作的,我从你的例子中提取了一段代码。

    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 功能,这正是您所拥有的。这里需要注意三件事,

    您将其声明为异步函数,因此它默认返回 Promise。 它可以像普通函数一样简单地返回任何东西,如果它是同步函数,请避免使用异步。 正如你所说,你不能从回调中返回任何东西。因为它是异步的,所以你必须返回 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 博客,以便等待处理错误。

【讨论】:

以上是关于Javascript,如何等待多个承诺[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有“快速失败”行为的情况下并行等待多个承诺? [复制]

Javascript - 异步等待和获取 - 返回值,而不是承诺?

jQuery Deferred - 等待多个 AJAX 请求完成 [重复]

处理多个承诺拒绝[重复]

等待但从未解决/拒绝承诺内存使用[重复]

javascript 回调,承诺,Asysc等待