【问题标题】:How to make an async function wait for database queries to finish before resolving? [duplicate]如何使异步函数在解析之前等待数据库查询完成? [复制]
【发布时间】:2021-05-10 13:36:08
【问题描述】:

所以我有:

async function functionName() {
  return await new Promise( async (resolve, reject) => {
    try { 
      //variables and such
      for (let i = 0; i < results.length; i++) {
        await geo.geocode('mapbox.places', address, async function (err, geoData) {
          await db.query(`INSERT INTO table (col1, col2, col3) VALUES($1, $2, $3) ON CONFLICT DO NOTHING`, ["default", val2, val3], (err, res) => {
            if (err) {
              console.error(err);
              return;
            }
          }
        }
      }
      resolve("resolved")
    } catch(e) {
      reject(err)
    }
  }
}

由于某种原因,当我调用此函数时,似乎该函数正在解析,但在解析完成后对数据库进行了查询。如何确保该函数仅在查询完成/数据库更新后才解析?

【问题讨论】:

    标签: javascript node.js database asynchronous promise


    【解决方案1】:

    您不能将普通的回调与 Promise 混合使用,并希望控制流程。当您向它们传递回调时,geo.geocode() 和 db.query() 等函数不会返回承诺,因此 await 没有任何用处。相反,您需要使用这些函数的承诺版本,或者创建一个承诺版本(如果尚不存在)。

    如果您使用支持 Promise 的 geocode 库的 beta 版本,并且您使用支持 Promise 的 sql 库版本,那么您可以这样编写代码:

    async function functionName() {
          //variables and such
          for (let i = 0; i < results.length; i++) {
            let geoData = await geo.geocode('mapbox.places', address);
            await db.query(`INSERT INTO table (col1, col2, col3) VALUES($1, $2, $3) ON CONFLICT DO NOTHING`, ["default", val2, val3]);
          }
    }
    

    但是,由于这个 sql 语句无论你在做什么循环迭代都是一样的,所以这里肯定缺少一些使用 geoData 的代码。


    如果您无法访问支持 Promise 的 geo.geocode 和 db.query 版本,那么您可以像这样自己手动对各个方法进行 Promisify:

    const util = require('util');
    const geo.geocodeP = util.promisify(geo.geocode);
    const db.queryP = util.promisify(db.query);
    
    async function functionName() {
        //variables and such
        for (let i = 0; i < results.length; i++) {
            let geoData = await geo.geocodeP('mapbox.places', address);
            await db.queryP(`INSERT INTO table (col1, col2, col3) VALUES($1, $2, $3) ON CONFLICT DO NOTHING`, ["default",
                val2, val3
            ])
        }
    }
    

    请记住,这些都不会使函数在返回之前“等待”。该函数在遇到第一个await 时仍会立即返回一个promise,调用者必须使用该promise 和await 或.then() 才能从函数中获得最终结果。不过,这允许您做的是,在 for 循环中对异步操作进行排序,以便它们按顺序发生,其中一个异步操作在下一次运行之前等待前一个完成。

    【讨论】:

    • 好吧,地理数据为您提供了您放入数据库列的值。我为 Postgres 查询函数找到了这个 (github.com/vitaly-t/pg-promise)……你认为这行得通吗?至于 .geocode() 函数,它内置在 Mapbox 中,我没有看到任何承诺版本——你有测试版的链接吗?我所做的只是 npm 安装 mapbox-geocoding 以访问该功能(而且它看起来像是一个非常古老的 api)
    • @nickcoding2 - 提到了测试版here。您还可以使用util.promisify() 来创建任何基于 nodejs 调用风格回调函数的 promisfied 版本。 await 要记住的关键是它只做一些有用的事情,如果它正在等待与你的异步操作相关的承诺,你想要什么。等待不返回承诺的东西什么都不做。
    • 您好,非常感谢您的帮助,但我正在使用 Mapbox。所以我可能必须同时制作地理编码功能和查询功能的承诺功能,对吧?同样,在使用 util.promisify() 对 promisified 函数进行了一些快速研究之后,它到底应该是什么样子?我查看了您附加的链接,并试图找出 .then 和 catch 版本以及它将如何帮助我......我是否将承诺的查询(及其 .then 和 catch)放在承诺的 .then 块中地理编码功能?
    • @nickcoding2 - 好的,我在答案中添加了其他示例,展示了如何手动承诺异步方法,以便您可以将 await 与那些返回承诺的方法一起使用。
    猜你喜欢
    • 1970-01-01
    • 2020-01-15
    • 1970-01-01
    • 1970-01-01
    • 2020-03-02
    • 1970-01-01
    • 2017-10-12
    • 1970-01-01
    • 2021-09-08
    相关资源
    最近更新 更多