在承诺之外抛出错误

Posted

技术标签:

【中文标题】在承诺之外抛出错误【英文标题】:Throw errors outside of promise 【发布时间】:2018-02-19 11:00:01 【问题描述】:

我有一个功能来登录一个应该返回 JSON 的用户。

const username = req.body.username;
const password = req.body.password;

if (!username) 
  throw new Error('Missing username');


if (!password) 
  throw new Error('Missing password');


User.findOne( username, password ).then(user => 
  res.json( user );
).catch(err => 
  res.json( err );
);

但是在 JSON 中不返回缺少用户名或缺少密码的错误。

我可以改成

const username = req.body.username;
const password = req.body.password;

if (!username) 
  res.json( err: 'Missing username' );


if (!password) 
  res.json( err: 'Missing password' );


User.findOne( username, password ).then(user => 
  res.json( user );
).catch(err => 
  res.json( err );
);

不过好像有点多余。

将其封装在 Promise 中的正确方法是什么?

【问题讨论】:

【参考方案1】:

在您的第一个解决方案中,将不会处理抛出的错误,因为您将它们抛出承诺链之外并且没有 try/catch 块。在您的第二个解决方案中,您可能会收到 cannot send headers after they sent 错误,因为可以发送两次响应(username 缺失和 password 缺失)。

所以这里可能的解决方案之一是创建一个承诺链(使用Promise.resolve())并在此处验证参数:

function validateParams() 
  const username = req.body.username;
  const password = req.body.password;

  if (!username) 
    throw new Error('Missing username');
  
  if (!password) 
    throw new Error('Missing password');
  
  return  username, password ;


Promise
  .resolve()
  .then(validateParams)
  .then(filter => User.findOne(filter))
  .then(user => res.json(user))
  .catch(err => res.json(err));

【讨论】:

在您的示例中,您应该包括对 validateParams 的调用 :) 对于阅读这篇文章的人来说,.then(validateParams) 相当于.then(() => validateParams())【参考方案2】:

显而易见的方法确实是将它们封装在一个 Promise 中以启动你的 Promise 链(User.findOne 在第一个 then 块内)——这样你当前的错误处理程序就可以很好地捕获它们。

【讨论】:

【参考方案3】:

我以@alexmac 为例并使用 es6 异步功能:

function validateParams() 
  const username = req.body.username;
  const password = req.body.password;

  if (!username) 
    throw new Error('Missing username');
  
  if (!password) 
    throw new Error('Missing password');
  
  return  username, password ;


async function resolver() 
    try 
        await resolve()
        let filter = validateParams()
        let user = await User.findOne(filter)
        await res.json(user)
     catch (e) 
        await res.json(e)
    
 

使用if 而不是throw 会看起来更优雅:

async function(req, res) 
    const password = req.body.password
    const username = req.body.username
    let c = !password ? 'missing password' : 
                !username ? 'missing username' : null
    if (!c) 
       c = await User.findOne( username, password )
    
    await res.json(c)

【讨论】:

【参考方案4】:

你可以将你的函数包装在一个 Promise 中并有效地处理它

function getRes()  
    return new Promise(function(resolve, reject)
        const username = req.body.username;
        const password = req.body.password;

        if (!username) 
          reject(new Error('Missing username'));
        

        if (!password) 
          reject(new Error('Missing password'));
        

        resolve(User.findOne( username, password ));
    );



getRes().then(function(result)
    res.json(result);
).catch(function(err)
    res.json(err);
)

【讨论】:

避免Promise constructor antipattern! 如果你坚持使用 new Promise 构造函数,要么将 User.findOne 承诺放在 then 处理程序之后,要么在最后调用 resolve(User.findOne(…))

以上是关于在承诺之外抛出错误的主要内容,如果未能解决你的问题,请参考以下文章

条纹元素抛出“未处理的承诺拒绝”

不能从异步承诺执行器函数中抛出错误

状态 200 OK(),角度承诺抛出错误——没有“访问控制允许来源”

如何在 catch 语句之外拒绝异步/等待?

财产在承诺中未定义,但在承诺之外工作

Oracle DataReader 在 GetDateTime 上出错