如何等待函数或数据库查询?然后处理结果,最后发回

Posted

技术标签:

【中文标题】如何等待函数或数据库查询?然后处理结果,最后发回【英文标题】:How to await for a function or a database query? And then process results and finally send them back 【发布时间】:2018-01-27 15:21:29 【问题描述】:

我有这种情况:

app.get('/', async function (request, response) 

     await foo(DbConnection).then((result) => console.log("response ready"));
)

let foo = async function (DbConnection)

    const myQuery = "SELECT * FROM my_table";

    DbConnection.query(myQuery, async function(err, results) 
       console.log("query ready");
       await bar(results).then((response) => console.log("bar done"))
    )
    return; // go back to app.get() to send stuff to client


let bar = async function (results)
   
    console.log("inside bar");
    await ThirdPartyAPI(result).then((value)) => return value);

简单地说:

我收到来自客户端的 GET 请求

我调用 foo() 来查询数据库并对结果应用函数

我使用第三方 API 处理结果,这需要时间来完成

我将最终结果发回给客户

我期待看到:

查询准备就绪 -> 内部栏 -> 栏完成 -> 响应准备好

但我看到的是:

响应就绪 -> 查询就绪 -> 内栏 -> 栏完成

客户端正在接收undefined,因为当response.send() 时没有准备好

可能是什么问题?

【问题讨论】:

【参考方案1】:

您的代码中的主要问题是您混合了async/await 语法和回调。仅当您想对该函数内部的异步调用结果执行某些操作时,才使用async 函数。

此外,针对您的情况,您需要promisify connection.query() - 创建一个承诺。请参阅下面带有工作代码的正确模式:

app.get('/', async (request, response) => 
    // waiting for the result of foo
    let result = await foo();
    console.log("response ready");
);

let foo = async () => 
    const myQuery = "SELECT * FROM my_table";

    // getting the result of the query
    let results = await new Promise((resolve, reject) => connection.query(myQuery, (err, results) => 
      if (err) 
        reject(err)
       else 
        resolve(results);
      
    ));
    console.log("query ready");
    // call bar and waiting the result
    let res = await bar(results);
    console.log("bar done");
    // return resolved promise
    return res;


let bar = (results) =>    
    console.log("inside bar");
    // just return a promise, we don't need the result here
    return ThirdPartyAPI(result);

关于使用async/await的说明async/await 的强大之处在于它允许您使用同步语言结构编写异步代码。但这并不意味着所有函数都应标记为asyncasync 将函数包装成 promise,如果函数执行同步代码,这种包装会导致错误(例如,您期望字符串,但函数返回已解析的 promise)。另一个例子,函数执行异步代码,但不使用中间结果,在这种情况下,只返回异步调用的结果就足够了。

您应该在以下情况下使用async/await

该函数执行 sync 代码,您希望将结果包装到一个 Promise 中(用于创建 Promise 链)。 该函数执行异步代码,您需要一个中间结果。 该函数执行异步代码,而您不需要需要中间结果,但您希望连续执行代码(例如,用于处理数组中的每个项目)。

让我们考虑一些例子:

1.sync 函数,验证userIdvalidateParams 返回 true/false,如果 userId 未定义,validateParams2 将引发错误。 validateParams3 返回一个 Promise,可用于创建 Promise 链:validateParams3().then(...)

let validateParams = (userId) => 
  return userId;


let validateParams2 = (userId) => 
  if (!userId) 
    throw new Error('userId is undefined');
  


let validateParams3 = (userId) => 
  if (!userId) 
    return Promise.reject(new Error('userId is undefined'));
  
  return Promise.resolve();

2.async 函数,不使用中间结果。 getUser 返回待处理的承诺(当userId 有效时)或已履行的承诺(当userId 未定义时)。它应该返回一个承诺,因为getUser 可用于启动承诺链:createUser().then(...)getUser2getUser3 做同样的事情,返回一个承诺。此处需要 promise 以避免出现 unhandledError 错误,因为 validateParams2 可能会抛出错误。 getUser2 makred 为 async(自动创建一个 Promise),getUser3 返回一个 Promise。

let getUser = (userId) => 
  if (validateParams(userId)) 
    return db.getUserById(userId);
  
  return Promise.resolve();


let getUser2 = async (userId) => 
  validateParams2(userId);
  return db.getUserById(userId);


let getUser3 = (userId) => 
  return Promise
    .resolve(userId)
    .then(validateParams2)
    .then(() => db.getUserById(userId);

3.async 函数,使用中间结果:

let updateUser = async (userId, userData) => 
  let user = await getUser(userId);
  _.extend(user, userData);

  return db.saveUser(user);

上面的函数可以这样使用:

// with async
let fn = async (userId, userData) =>  
  try 
    let user = await updateUser(userId, userData);
    console.log(user);
  
  catch (err) 
    console.log(err);
  


// without async
let fn2 = (userId, userData) =>  
  updateUser(userId, userData)
    .then(user => console.log(user))
    .catch(err => console.log(err))
  

【讨论】:

谢谢 Alex,您能否解释或提供有关“仅当您想在此函数内对异步调用的结果执行某些操作时才使用异步函数的更多信息/示例。 " 因为我将更改标题以获取更一般的主题以帮助其他用户。我真的很感激,因为这对我来说也是一个学习过程。 @KhalilKhalaf 添加了有关使用 async/await 的说明。 我不明白你为什么改变了我的例子,并说它不起作用?为什么在connection.query 中添加回调?您还在bar 中添加了records.map...所以目前这是完全不同的问题。我的回答是针对原始问题。 我明白了,我知道问题出在哪里connection.query 没有返回承诺(我以为它会返回......)。你需要promisify connection.query - 创建一个promise。更新了代码。 :),很高兴能帮到你,connection.query 让我有点困惑。

以上是关于如何等待函数或数据库查询?然后处理结果,最后发回的主要内容,如果未能解决你的问题,请参考以下文章

java 异步查询转同步多种实现方式:循环等待,CountDownLatch,Spring Even

weblogic部分配置信息如何查询

在返回函数的变量之前,如何等待 promise 完成?

SQL server中用一段语句得到的查询结果 怎么当成一张新表进行更新操作

SQL查询结果替换???????

LoadRunner常见测试结果分析