Error: Callback was already called in loopback

Error: Callback was already called in loopback

我有以下代码:

"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,但它导致了同样的错误。如果我只留下在捕获代码中调用的下一个,它不会抛出错误。

有什么想法吗?谢谢!

您应该将 sendEmail 方法声明为异步的,因为它 returns 是一个承诺。

异步函数发送电子邮件(营地){ ... }

如果您使用的是异步函数,则不应显式调用 next,它 会自动调用。

check out this github issue for 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 中,除非你想 modify/work 处理错误。

阅读 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();
       }
   })
}

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

但是当我 运行 端点加载 resource.Like 时警告问题仍然出现 http://localhost:3000/api/MyModel 以前,只有当 before save 操作挂钩被触发时才会出现此问题。

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

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

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

是什么原因导致了这个问题,如何解决?