当前位置: 首页 > 知识库问答 >
问题:

为什么多层异步函数不能捕获节点中最低级别抛出的错误?

袁开宇
2023-03-14

我正在尝试测试一些邮件代码的失败模式,在最低级别可能会抛出一个错误。测试和抛出函数之间的所有层都是异步的,并在它们下面的函数上使用await。在顶层(同样在异步函数中,我有一个try catch块。但是,在错误传播到此级别之前,节点正在引发未处理的promise异常。

我的测试代码如下所示

beforeEach(function() {
  //set default values - tests can change them
  this.reasons = '';
  this.reschedules = 0;
  this.params.cid = 35124;

  this.startTest = async () => {
    /*  this.confirmation is an async function under test, 
        this.mailer is a mock mailer with an async "send" method
        which will throw an error in the correct test */
    const doner = this.confirmation(this.mailer);  
    // ..other actions related to mocking database access made by confirmation
    await doner;
    return this.mailer.maildata; //provide info on parameters passed to this.mailer
  };
});
it('Failure to send is reported', async function() {
  this.mailer.sendResolve = false; //tell mock mailer to fail send request
  try {
    await this.startTest();
    expect(true).to.be.false;
  } catch(err) {
    expect(err).to.be.instanceOf(Error);
  }
});

模拟邮件程序有点像这样

class Mailer {
    constructor(user,params){
...
    }
    ...
    async send(subject, to, cc, bcc) {
      this.maildata.subject = subject;
      if (to !== undefined) this.maildata.to = to;
      if (cc !== undefined) this.maildata.cc = cc;
      if (bcc !== undefined) this.maildata.bcc = bcc;
      if (!this.sendResolve) throw new Error('Test Error');
    }
    ...
 }

以及测试代码的摘要

 module.exports = async function(mailer) {
    //get confirm data from database
    const cData = await confirm(mailer.params.cid, mailer.db);
    if (cData.count > 0) {
       // ... format the email message and build it into maildata
       await mailer.send(
        subject,
        emailAddress,
        null,
        process.env.PAS_MAIL_FROM,
        {
          pid:cData.pid,
          type: 'confirmation',
          extra: `Calendar ID ${mailer.params.cid} with procedure ${cData.procedure}`
        }
      );
      debug('message sent, update the database');
      await mailer.db.exec(async connection => {
 ...
       });
      debug('success');
    } else {
      debug('invalid calendarid');
      throw new Error('Invalid Calendar ID');
    }
  };

可以看出,从异步发送函数的调用路径将堆栈抛回try{}catch(){}都是异步函数。但是当我运行这个测试节点时,会输出一个未处理的promise拒绝。

我尝试过使用可视化工作室代码调试器来单步执行此步骤,我有点迷失在包装异步函数以将它们转换为promise提供者的机器中。据我所知,一层错误被正确处理,然后在下一层失败。

这是不是意味着每个异步函数都必须有一个try catch块来捕捉并重新抛出任何错误?我找不到任何解释说我必须这么做。

共有1个答案

百里涛
2023-03-14

回答你的问题:

这是否意味着每个异步函数都必须有一个try catch块来捕获和重新抛出任何错误?

错误通过<code>发出,并等待-ed调用,如您所期望的:

const assert = require('assert');

const outer = async () => {
  await middle();
}

const middle = async () => {
  await inner();
}

const inner = async () => {
  throw new Error('something bad happened');
}

it('should catch the error', async () => {
  let errorMessage;
  try {
    await outer();
  }
  catch (err) {
    errorMessage = err.message;
  }
  assert(errorMessage === 'something bad happened');  // Success!
});

...所以不,你不需要在每一层都有一个< code>try / catch块。

追踪未处理的promise拒绝

我看不到wait链在您的示例代码中的确切位置可能会被破坏,但为了帮助跟踪未处理的Promise拒绝,您可以为unhandledReject事件添加一个进程处理程序,并查看记录的Promise以查看拒绝的开始位置,并从那里通过调用堆栈向后跟踪:

const assert = require('assert');

const outer = async () => {
  await middle();
}

const middle = async () => {
  inner();  // <= this will cause an Unhandled Rejection
}

const inner = async () => {
  throw new Error('something bad happened');
}

it('should catch the error', async () => {
  let errorMessage;
  try {
    await outer();
  }
  catch (err) {
    errorMessage = err.message;
  }
  assert(errorMessage === undefined);  // Success!  (broken await chain)
})

process.on('unhandledRejection', (reason, p) => {
  console.log('Unhandled Rejection at:', p);
  console.log('reason:', reason);
});

…在这种情况下记录:

Unhandled Rejection at: Promise {
  <rejected> Error: something bad happened
      at inner (.../code.test.js:12:9)
      at inner (.../code.test.js:8:3)
      at middle (.../code.test.js:4:9)  // <= this is the broken link
      at Context.outer (.../code.test.js:18:11)
      at callFn (...\node_modules\mocha\lib\runnable.js:387:21)
      ...

…这将我们指向内部中抛出的错误,通过跟踪链,我们发现中间是断开的链接。

 类似资料:
  • 当捕获异常时,其余代码仍会执行。 我试图用这种方式来捕捉异常,而不是尝试捕捉 我的第一个问题是如何在错误被捕获后阻止代码到达其余代码。 第二个问题是,如果我将Promise reject替换为注释掉的抛出错误,抛出的异常错误将不会被捕获,这是为什么?

  • 在try块中为未抛出异常子类的方法捕获异常,将无法编译。当我捕捉到异常时,它起作用了。它是如何工作的??

  • 蓝牙支持有什么问题吗? 你还知道什么问题吗?

  • 我有一个类(我不能修改),它只有一个构造函数,它接受一个参数,如下所示: 由于构造函数可以抛出错误,我想实现错误处理。我的第一次尝试是: 但是,现在在块的范围内,不能在其他地方使用。如果我理解正确,我不能在没有初始化的情况下声明对象,因此我可以在块之外声明。那么,我该如何捕捉构造函数抛出的错误呢? 编辑:为了澄清,这是在我的中,在中我将中止程序。此外,参数是一个将被打开的文件,因此没有已知的安全输

  • 编译器知道检查的异常不能在安全方法内抛出-所以也许它应该允许只捕获未检查的异常? 回到主要问题--有没有理由以这种方式实现捕获检查异常?这仅仅是设计中的一个缺陷还是我遗漏了一些重要的因素--也许是向后的不兼容性?在此场景中,如果只允许捕获,可能会出现什么问题?实例非常感谢。

  • 下面是我的代码。当我运行它时,我在线程“main”java.lang.IndexOutOfBoundsException:Index:3、Size:2中得到异常,而不是我的异常消息。谁能解释一下我做错了什么,为什么会这样?谢谢!