使用async parallel启动时报UNHANDLEDREJECTION回调已被调用错误如何解决
错误根因
你遇到的Callback was already called.错误本质是async.parallel的同一个任务回调被多次调用,或者部分任务的回调未按约定格式调用,核心问题有3个:
- 不同任务的错误回调格式不统一:task1错误时用
callback(err, null)符合async约定,但task2、task3、task5错误时用了callback(null, err),把错误对象当成成功返回值传递,导致async无法正确识别任务失败,逻辑混乱。 - 数据库查询回调缺少兜底分支:所有查询回调仅处理了
err和查询结果存在两种场景,如果查询成功但返回空值(比如Mongo未查到匹配数据返回null),会导致任务回调永远不会被触发,或者后续逻辑异常触发重复调用。 - 回调式方法与async/await混用不兼容:
loadMongoData是回调风格的方法,不返回Promise,你在init中用await调用不会等待加载完成,可能导致逻辑时序错误。
修复方案
1. 统一所有任务的回调格式,补充兜底分支
修正所有任务的回调逻辑,确保每个任务的回调有且仅被调用一次,示例(以task1、task2为例,其余task3、task5同理修改):
// 修正后的task1 task1: function (callback) { CriteriaDef.find({}) .lean() .exec(function (err, criteriaDefs) { if (err) { console.log('Inside err 1') logger.error('Error fetching CriteriaDef: ', util.inspect(err)); // 错误直接返回,第一个参数传err return callback(err); } // 兜底分支,不管结果是否存在都执行 console.log('Inside criteriaDefs') if (criteriaDefs?.length > 0) { console.log('Inside criteriaDefs 1') global.CRITERIA_DEFS = criteriaDefs; cache['criteria_defs'] = criteriaDefs; } callback(null, null); }); }, // 修正后的task2 task2: function (callback) { groupDef .find({}) .lean() .exec(function (err, groupDefs) { if (err) { console.log('Inside err2') logger.error('Error fetching groupDefs: ', util.inspect(err)); // 错误直接返回,第一个参数传err return callback(err); } console.log('Inside ') global.groupDefsWithRoles = groupDefs || []; let _groupDefs = []; _.each(groupDefs || [], function (groupDef) { var data = { value: groupDef._id, label: `${groupDef._id}: ${groupDef.description}` }; _groupDefs.push(data); }); global.groupDefs = _groupDefs; cache['groupDefs'] = _groupDefs; logger.info('Loaded groupDefs: ', global.groupDefs.length); callback(null, null); }); },
2. 适配async/await调用,将loadMongoData改为返回Promise
修改loadMongoData方法,用Promise包装异步逻辑,适配init中的await调用:
loadMongoData(env) { const _this = this; console.log('Inside loadMongoData') // 返回Promise适配async/await return new Promise((resolve, reject) => { async.parallel( { // 上面修正后的6个任务逻辑 }, function (err, results) { if (err) { console.log('Inside final error') logger.error("Something went wrong can't start the app: ", util.inspect(err)); return reject(err); } console.log('Inside final else') logger.info('loaded all globals properly :)'); resolve(); } ) }) }
3. 校验第三方方法回调逻辑
检查sisp.loadProducts方法的实现,确保它传入的回调只会被调用一次,不会内部触发多次回调导致错误。
内容的提问来源于stack exchange,提问作者Pravin Kottawar
相关产品推荐
相关产品推荐

