异步/等待错误处理

5

我正在尝试处理我的异步方法抛出的自定义错误,但是try catch块不能正确工作。

我认为我这样做应该可以解决问题,但是错误未被捕获,程序终止并在终端中显示。

这是它抛出错误的地方:

async setupTap(tap) {
  const model = this.connection.model('Tap', TapSchema);

  await model.findOneAndUpdate({ id: tap.id }, tap, (err, result) => {
    let error = null;
    if (!result) {
      throw new Error('Tap doesn\'t exists', 404);
    }
    return result;
  });
}

然后,错误处理代码:
async setupTapHandler(request, h) {
  const tapData = {
    id: request.params.id,
    clientId: request.payload.clientId,
    beerId: request.payload.beerId,
    kegId: request.payload.kegId,
  };

  try {
    await this.kegeratorApi.setupTap(tapData);
  } catch (e) {
    if (e.code === 404) return h.response().code(404);
  }

  return h.response().code(204);
}

有人能帮助我吗?

我也看了其他的话题:

使用async/await正确的Try...Catch语法

如何在async/await情况下正确实现错误处理


3
你为什么认为new Error("string", 404)会导致e.code == 404 - Jaromanda X
你是如何调用这个函数的? - Rahul Sharma
3
注意,你正在回调函数中抛出(和返回)内容到findOneAndUpdate函数 - 所以,无论如何都不会按预期工作。 - Jaromanda X
1
只需参考@JaromandaX的评论,您就有解决方案了。 - Akash Dathan
2个回答

3
如果你正在等待一个异步操作,只有在等待一个Promise时才能成功使用await。假设你正在使用mongoose,我不太了解mongoose,但是如果你传递一个回调函数,model.findOneAndUpdate()似乎不会返回一个Promise,而是执行并将结果放在回调函数中。
此外,从这样的回调函数中使用throw只会将其抛出到数据库中(调用回调函数的代码),对你没有任何帮助。要使throw创建一个拒绝的Promise,你需要从异步函数的顶层抛出或者从.then().catch()处理程序内部或在Promise执行程序函数内部抛出。这就是throw使Promise变为rejected的地方。
关键在于,你想使用数据库的Promise接口,而不是回调接口。如果不传递回调函数,则会返回一个查询对象,你可以在上面使用.exec()获得一个Promise,然后就可以使用await了。
此外,你没有构建一个错误对象,它应该将.code属性设置为404。这不是Error对象构造函数支持的属性,所以如果你想拥有该属性,就必须手动设置它。
我建议使用以下方式:
async setupTap(tap) {
    const model = this.connection.model('Tap', TapSchema);

    let result = await model.findOneAndUpdate({ id: tap.id }, tap).exec();
    if (!result) {
        let err = new Error('Tap doesn\'t exists');
        err.code = 404;
        throw err;
    }
    return result;
}

或者,只有一个异步操作在这里,使用await没有太多好处。您可以这样做:

setupTap(tap) {
    const model = this.connection.model('Tap', TapSchema);

    return model.findOneAndUpdate({ id: tap.id }, tap).exec().then(result => {
        if (!result) {
            let err = new Error('Tap doesn\'t exists');
            err.code = 404;
            throw err;
        }
        return result;
    });
}

1
函数findOneAndUpdate返回一个promise,因此不需要回调。如果需要回调且无法升级到较新版本,则可以在 将调用包装在promise中(在要将回调API用作Promise,您可以执行以下操作:下)。
然后,如果要在错误上设置代码,则不能使用构造函数。
async setupTap(tap) {
  const model = this.connection.model('Tap', TapSchema);

  const result =   await model.findOneAndUpdate({ id: tap.id }, tap);
  if (!result) {
    const e = new Error('Tap doesn\'t exists');
    e.code = 404;
    throw(e);
  }
  return result;
}

async setupTapHandler(request, h) {
  const tapData = {
    id: request.params.id,
    clientId: request.payload.clientId,
    beerId: request.payload.beerId,
    kegId: request.payload.kegId,
  };

  try {
    await this.kegeratorApi.setupTap(tapData);
  } catch (e) {
    if (e.code === 404) return h.response().code(404);
  }

  return h.response().code(204);
}

想太多了。如果测试只是检查“result”是否为空/假,并且唯一的结果是发送404,则所有代码都是过度设计。检查我的答案,你会发现它产生相同的结果,但更简单。 - Randy Casburn
findOneAndUpdate() 不是返回一个 Promise,而是返回一个查询吗? - jfriend00
@jfriend00 不确定,这就是为什么我添加了将回调包装为promise的链接,但是此文档没有提到.exec作为promise启动,旧版本或其他库可能需要它,如果库版本足够旧,则可能只有回调。 - HMR
1
好的,我猜这取决于OP使用的是哪个版本的数据库。我在这里查看了mongoose文档:http://mongoosejs.com/docs/api.html。无论哪种情况,OP都必须停止使用回调函数,并使用`await`的promise接口来完成其工作。 - jfriend00

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接