错误:已在回调中调用回调

时间:2018-05-28 08:04:58

标签: node.js express promise async-await loopbackjs

我有以下代码:

"use strict";
const Raven = require("raven");
Raven.config(
    "test"
).install();

module.exports = function(Reservation) {
  function dateValidator(err) {
    if (this.startDate >= this.endDate) {
      err();
    }
  }

  function sendEmail(campground) {
    return new Promise((resolve, reject) => {
      Reservation.app.models.Email.send(formEmailObject(campground), 
        function(
          err,
          mail
        ) {
           if (err) {
            console.log(err);
            Raven.captureException(err);
            reject(err);
           } else {
             console.log(mail);
             console.log("email sent!");
             resolve(mail);
           }
       });
   });
} 

  function formEmailObject(campground) {
    return {
      to: "loopbackintern@yopmail.com",
      from: "noreply@optis.be",
      subject: "Thank you for your reservation at " + campground.name,
      html:
        "<p>We confirm your reservation for <strong>" +
        campground.name +
        "</strong></p>"
    };
   }

Reservation.validate("startDate", dateValidator, {
 message: "endDate should be after startDate"
});

Reservation.observe("after save", async function(ctx, next) {
 try {
  const campground = await Reservation.app.models.Campground.findById(
    ctx.instance.campgroundId
  );
  const mail = await sendEmail(campground);
  next();
 } catch (e) {
  Raven.captureException(e);
  next(e);
 }
});
};

抱歉格式不佳。当流程完成后,我收到此错误:

(node:3907) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Callback was already called.

我在两个地方调用next()回调,一个在try代码中,另一个在catch代码中。我假设当一切正常时,下一个回调只被调用一次,并且当它出错时也是如此。但它似乎被称为两次,我不知道为什么。

我还尝试在try / catch代码之外调用next,但它会导致相同的错误。如果我只留下在catch代码中调用的下一个,它就不会抛出错误。

有什么想法吗?谢谢!

3 个答案:

答案 0 :(得分:1)

如果您使用的是异步功能,则不应明确调用下一个,会自动调用。

  

查看此github issue以获取loopback async / await

所以你的钩子可能如下所示。

 Reservation.observe("after save", async ctx => {
  try {
    const campground = await Reservation.app.models.Campground.findById(
      ctx.instance.campgroundId
    );
    const mail = await sendEmail(campground);
  } catch (e) {
    Raven.captureException(e);
    throw e;
  }
});

注意:除非你想修改/使用错误,否则你不需要将它包装在try catch中。

答案 1 :(得分:0)

您应该将sendEmail方法声明为async,因为它返回一个promise。

async function sendEmail(campground){ ... }

答案 2 :(得分:0)

在阅读this article之后,我创建了一个包含以下代码的await-handler.js文件。

module.exports = (promise) =>
  promise
    .then(data => ({
        ok: true,
        data
    }))
    .catch(error =>
      Promise.resolve({
        ok: false,
        error
      })
    );

然后在MyModel.js文件中,我创建了一个异步函数来从数据库中获取值,如下所示。

const awaitHandler = require("./../await-handler.js")
const getMaxNumber = async (MyModel) => {
    let result = await awaitHandler(MyModel.find());
    if (result.ok) {
        if (result.data.length) {
            return result.data.reduce((max, b) => Math.max(max, b.propertyName), result.data[0] && result.data[0].propertyName);
        } else {
            return 0;
        }
    } else {
        return result.error;
    }
}

根据@ Mehari的回答,我已经评论了对next()方法的调用,如下所示: -

module.exports = function(MyModel) {
   MyModel.observe('before save', async(ctx, next) => {
       const maxNumber = await getMaxNumber (MyModel);
       if(ctx.instance) {
            ...
            set the required property using ctx.instance.* 
            like createdAt, createdBy properties
            ...
            // return next();
       } else {
            ...
            code for patch
            ...
            // return next();
       }
   })
}

这解决了触发保存端点时的警告问题。

但是当我运行端点来加载资源时,仍会出现警告问题。喜欢      http://localhost:3000/api/MyModel 以前,只有在触发before save操作挂钩时才会出现此问题。

遇到此问题后,我检查了添加accessloaded操作挂钩,我发现警告是在loaded操作挂钩后发出的。

MyModel.observe('access', (ctx, next) => {
    return next();
})

MyModel.observe('loaded', (ctx, next) => {
    return next();
})

可能导致此问题的原因以及如何解决?