pg-promise:返回查询结果的正确方法

Posted

技术标签:

【中文标题】pg-promise:返回查询结果的正确方法【英文标题】:Pg-promise: proper way of returning result of query 【发布时间】:2018-10-14 01:47:07 【问题描述】:

我想使用pg-promise检查用户名是否已在使用中。

我使用以下查询:

this.db.one('SELECT EXISTS(SELECT 1 FROM users WHERE username = $1)', username);

我试图将此查询封装在一个函数中,如果用户名存在则返回 true,否则返回 false。

类似:

existsUsername(username)
  this.db.one('SELECT EXISTS(SELECT 1 FROM users WHERE username = $1)', username)
         .then(data => 
           if(data.exists == true)
             return true;
            else 
             return false;
           
         );

我可以像这样简单地使用:

if(db.users.existsUsername(username))
  // this username is already taken

但是 if 条件在查询完成之前被评估,导致一个未定义的变量。

返回查询结果的正确方法是什么?

编辑:外部调用者执行多个异步检查并返回用户是否有效:

function signUp(username, email, ...)
  // perform username existence check existsUser(username)

  // perform email existence check existsEmail(username)

  // ...

  // if everything OK, put user in DB

【问题讨论】:

U 似乎忘记在 existsUsername 中返回 【参考方案1】:

您不能以同步方式使用异步操作,您还需要重写检查用户是否以异步方式存在的代码。即:

// returns a promise 
function existsUsername(username)
   return this.db.one('SELECT EXISTS(SELECT 1 FROM users WHERE username = $1)', username);

然后在应用程序中以类似的方式使用它

db.users.existsUsername(username)
   .then( data => 
      data.exists ? handleUserExistsAsync() : handleUserNotExistsAsync();
   )
   .catch( err => 
      // some err occurs, db fail or something
      // however, you can catch it in an upper level
   );

编辑:将Promise.all 用于多项任务,可能会出现性能和连接问题(如 Vitaly 所述)。

最好在db.task里面使用db.batch

【讨论】:

感谢您的回答。但是由于我有多个这样的查询(existsUsername、existsEmail 等),我怎么知道所有异步操作都已完成并且我可以继续执行调用者的 return 语句? 是的,使用 Promise.all([checkUserName(), checkEmail(), checkWhateverElse()]).then( arrayOfChecksResults => // 检查数组是否正常,并保存用户) 除了 Promise.all 还有其他方法来管理一堆 Promise,你可以检查哪一个更适合你 这正是我想要的!谢谢! 您永远不应该将Promise.all 与数据库方法一起使用,因为它们最终都会针对单独的连接执行,这对性能不利。见Chaining Queries。相反,您应该使用带有batch 的任务。【参考方案2】:

最简单的方法:

existsUsername(username) 
  return this.db.oneOrNone('SELECT * FROM users WHERE username = $1 LIMIT 1', username, a => !!a);

然后使用它:

db.users.existsUsername(username)
   .then(exists => 
      // exists - boolean
   )
   .catch(error => 
   );

你不能做if(db.users.existsUsername(username)) 之类的事情,这会将同步代码与异步代码混为一谈。但是,如果您可以使用 ES7 语法,您可以使用 if(await db.users.existsUsername(username))


如果你有三个这样的独立函数(checkUserNamecheckEmailcheckWhateverElse),并且想要全部执行,那么最好的方法是:

db.task(t => 
   return t.batch([checkUserName(t), checkEmail(t), checkWhateverElse(t)]);
)
.then(data => 
    // data = result of the batch operation;
)
.catch(error => 
   // error
);

与 ES7 语法相同:

db.task(async t => 
   const a = await checkUserName(t);
   const b = await checkEmail(t);
   const c = await checkWhateverElse(t);
   return a, b, c;
)
.then(data => 
    // data = a, b, c object;
)
.catch(error => 
   // error
);

注意:每个函数都应针对t - 任务上下文执行查询,以便共享连接。

【讨论】:

以上是关于pg-promise:返回查询结果的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

将嵌套循环查询组合到父数组结果 - pg-promise

pg-promise:在事务中的下一个查询中使用一个查询的结果

如何使用pg-promise同时从多个查询中获得结果?

pg-promise 将整数作为字符串返回

使用 pg-promise 记录特定的 postgresql 查询

Pg-promise插入/事务在异步队列中不起作用