我使用的是Node.js和异步库,但我一直看到错误:Callback was already called
。
我想我理解为什么会收到错误,但我不知道是否可以执行以下操作/如何解决。
基本上我希望在外部回调完成之前完成两个内部回调。
所以我面对这个问题的代码如下:
async.forEachLimit(inData, 25, function (data, innercallback) {
myJson.matches.forEach(function (oMatches) {
if (data.$.id == oMatches.SourceId) {
oMatches.ids.forEach(function (odId) {
client.execute("g.addV('test').property('id', \"" + data.$.id + "\")", {},
function (err) {
setTimeout(function () { innercallback(err) }, 2000);
});
client.execute("g.V('" + data.$.id + "').addE('matches').to(g.V('xyz'))", {},
function (err) {
setTimeout(function () { innercallback(err) }, 2000);
});
})
} //there is no else case.
});
}, outercallback);
顺便说一下 - 我使用setTimeout
和async.forEachLimit
来减少对Azure的请求数量(因为我没有太多)
答案 0 :(得分:0)
Promise可用于确保异步回调的顺序。
看看async#AsyncFunction
in the official document
async
包
无论我们何处接受节点式异步功能,我们也直接接受 接受ES2017异步功能。在这种情况下,异步功能将 不会传递最终的回调参数,并且任何抛出的错误都将是 用作隐式回调的错误参数,以及返回 value将用作结果值。 (即拒绝了 返回Promise成为错误的回调参数,并解决了 价值成为结果。)
async#forEachLimit
的第三个参数是async#AsyncFunction
,因此您可以从中返回一个承诺,然后解决承诺,表明其工作已经完成。
您的代码可以改进如下,
async.forEachLimit(inData, 25, function (data, innercallback) {
// we will wait for all async function in here to complete
// then call the innercallback
var asyncPromises = []
myJson.matches.forEach(function (oMatches) {
if (data.$.id == oMatches.SourceId) {
oMatches.ids.forEach(function (odId) {
asyncPromises.push(new Promise(function (resolve, reject) {
client.execute("g.addV('test').property('id', \"" + data.$.id + "\")", {},
function (err) {
setTimeout(function () {
if (err) {
reject(err)
return
}
resolve();
}, 2000);
});
}))
asyncPromises.push(new Promise(function (resolve, reject) {
client.execute("g.V('" + data.$.id + "').addE('matches').to(g.V('xyz'))", {},
function (err) {
setTimeout(function () {
if (err) {
reject(err)
return
}
resolve();
}, 2000);
});
}))
})
} //there is no else case.
})
// Here we can ensure that innercallback is called only for once
Promise.all(asyncPromises)
.then(function () {
innercallback(null)
})
.catch(function (err) {
// handle the error here
innercallback(err)
})
}, outercallback);
请注意,您应该确保在Node环境中获得Promise
支持。在Node v6之后支持内置Promise
。查看here。
<强>更新强>
我误解了你在调用outercallback之前必须完成的内部回调。我已使用Promise.all
更正了它,它将Promise
数组作为参数并返回Promise
,如果所有子Promise
全部已解析或如果其中一个子Promise
被拒绝,则被拒绝。请参阅Promise#all
。
更新(2018.05.14)
您必须确保从innercallback
包中的AsyncFunction(即async#forEachLimit
的第三个参数)收到的每个async
在每个包中仅被一次调用迭代。特别是在每次迭代中执行Array#forEach
时要小心,因为它可能会让您在迭代中多次调用innercallback
。
我更新了上面的代码块,我从innercallback
的回调中删除了对client.execute
的所有调用,并将所有client.execute
放入了承诺中。之后,我将所有承诺收集到asyncPromises
数组中。
Promise#all
用于确保所有承诺都已解决(即所有client.execute
已完成),然后最终调用innercallback
。或者,如果上述承诺之一被拒绝并被Promise#catch
捕获,请使用第一个参数调用innercallback
作为错误原因。
答案 1 :(得分:0)
如果两个innercallback
抛出错误,您正在调用client.execute
两次。您可以使用async.parallel函数或Promise.all
,这是async.parallel
的示例,您还需要在innercallback
块中调用else
函数
async.forEachLimit(inData, 25, function(data, innercallback) {
async.eachSeries(myJson.matches, function(oMatches, callback) {
if (data.$.id == oMatches.SourceId) {
async.eachSeries(oMatches.ids, function(odId, callback) {
async.parallel([
function(callback) {
client.execute("g.addV('test').property('id', \"" + data.$.id + "\")", {}, callback);
},
function(callback) {
client.execute("g.V('" + data.$.id + "').addE('matches').to(g.V('xyz'))", {}, callback);
}
], callback);
}, callback)
} else {
callback();
}
}, innercallback);
}, outercallback);
更新更新的代码,现在在async.eachSeries
Array.forEach