Loopback框架中Error: Callback was already called问题求助
解决Loopback中"Callback was already called"错误的问题
咱们先拆解下你遇到的问题:你在Loopback的after save observer里用了async/await,同时又手动调用了next()回调,这就导致了回调被重复调用的错误。
问题根源
Loopback的observe方法对async函数和传统回调函数的处理逻辑是不一样的:
- 如果你用传统的非async回调(带
next参数),必须手动调用next()来通知Loopback继续执行流程,或者调用next(err)传递错误。 - 但如果你把回调写成
async函数,Loopback内部会自动把这个函数包装成Promise处理器:它会等待async函数执行完成,如果函数顺利执行完(没有抛出错误),Loopback会自动触发流程继续;如果函数抛出错误,Loopback会自动捕获并处理这个错误。
你现在的代码既用了async函数,又手动调用了next(),相当于同时触发了两次流程通知——一次是async函数执行完成后Loopback自动触发的,另一次是你手动调用next()触发的,所以就出现了Callback was already called的错误。
解决方法
只需要二选一:要么用传统回调,要么用async函数,不要混合使用。针对你的代码,最简洁的修正就是去掉next参数,不再手动调用next(),让async函数的Promise自动处理流程:
修正后的代码:
"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" }); // 修正后的observe回调:移除next参数,不再手动调用next() Reservation.observe("after save", async function(ctx) { try { const campground = await Reservation.app.models.Campground.findById( ctx.instance.campgroundId ); const mail = await sendEmail(campground); // 不需要调用next(),async函数执行完毕后Loopback会自动继续流程 } catch (e) { Raven.captureException(e); throw e; // 抛出错误,Loopback会自动捕获并处理 } }); };
对你之前尝试的解释
- 把next()放在try/catch外部:不管是成功还是失败,你手动调用了next(),同时async函数的Promise完成时Loopback又会自动触发一次,所以还是会重复调用。
- 仅保留catch中的next():当try块执行成功时,你没有手动调用next(),Loopback通过async函数的resolve自动处理了流程,所以没有错误;但这种写法不规范,因为你混合了两种模式,正确的做法是完全移除next相关的代码。
内容的提问来源于stack exchange,提问作者Jorge Belano Murphy
相关产品推荐
相关产品推荐

