等待循环完成callback

来自PHP的背景,我试图让我的头在这个callback的东西。

基本上我想获得一些行,然后我想循环这些行,并检查他们对其他模型(不同的数据库)。 我希望回拨等待,直到他们都被循环和检查。

在sequelize遍历所有结果之前调用callback函数。

基本上我想要的function是“阻止”。 我需要改变什么?

toexport.getlasttransactions = function(lower,upper,callback){ var deferred = Q.defer(); var transactionsToUpdate = []; /////////////////////////// // set import conditions // /////////////////////////// var lowerbound = (lower) ? lower.format() : moment.utc().subtract(10, 'minutes').format(); var upperbound = (upper) ? upper.format() : moment.utc().format(); /////////////////////////////// // get IDs From Failed syncs // /////////////////////////////// FailedSync.find({ limit: 100 }) .then(function(res){ var FailedIDs = []; _.each(res, function(value,index){ FailedIDs.push(value.transaction_id); }); // build condition var queryCondition = { where: { updated_at: { between: [lowerbound,upperbound] } }, limit: 3 }; if(FailedIDs.length > 0){ queryCondition = { where: Sequelize.and({ updated_at: { between: [lowerbound,upperbound] } }, Sequelize.or( { id: FailedIDs } )) } } ////////////////////////////// // get Phoenix Transactions // ////////////////////////////// PhoenixTransaction .findAll(queryCondition) .then(function(poenixTrx){ _.each(poenixTrx, function(value, index){ Transaction.findOne({ where: { id: value.id }}) .then(function(result){ if(!result || result.length === 0){ transactionsToUpdate.push(value); console.log('!result || result.length === 0') } else if(result && result.length === 1){ if(result.hash != value.hash){ transactionsToUpdate.push(value); console.log('result.hash != poenixTrx[i].hash') } } }) .catch(function(err) { console.log(err) }) }) deferred.resolve(transactionsToUpdate); }) .catch(function(err){ throw new Error("Something went wrong getting PhoenixTransaction") }) }) deferred.promise.nodeify(callback); return deferred.promise; } 

用户在代码中有许多新的承诺:

  • 当你不需要的时候,你正在使用延期。
  • 您不使用承诺汇总方法
  • 你不是在适当的地方等着东西,而是在嵌套。

承诺是一段时间价值 。 您可以使用承诺, then在稍后的时间访问他们的结果,而不仅仅是马上 – Sequelize的承诺是基于蓝鸟,并提供丰富的API,为您做聚合。 这是一个注释版本的清理代码 – 注意它不是嵌套:

 toexport.getlasttransactions = function(lower,upper){ // no need for callback var lowerbound = (lower || moment.utc().subtract(10, 'minutes')).format(); var upperbound = (upper || moment.utc()).format(); // use `map` over a `each` with a push. var failedIds = FailedSync.find({ limit: 100 }).map(function(value){ return value.transaction_id; }); // build condition. var queryCondition = { where: { updated_at: { between: [lowerbound,upperbound] } }, limit: 3 }; var query = failedIds.then(function(ids){ // use promise as proxy if(ids.length === 0) return queryCondition; return { // You can return a value or a promise from `then` where: Sequelize.and({ updated_at: { between: [lowerbound,upperbound] } }, Sequelize.or({ id: ids}); }; }); var pheonixTransactions = query.then(function(condition){ return PhoenixTransaction.findAll(queryCondition); // filter based on result }); return pheonixTransactions.map(function(value){ // again, map over each return Transaction.findOne({ where: { id: value.id }}); // get the relevant one }).filter(function(result){ // filter over if chain and push return (!result || result.length === 0) || ((result && result.length === 1) && result.hash != value.hash); }); }; 

理想情况下,您可能希望使用诸如Bluebird的reduce之类的承诺数组,但我会提供一个async.series实现,因为它更容易理解。

安装asynchronous

 npm install async 

要求在你的文件中

 var async = require('async') 

然后执行它:

  ////////////////////////////// // get Phoenix Transactions // ////////////////////////////// PhoenixTransaction .findAll(queryCondition) .then(function(poenixTrx){ var queryArray = poenixTrx.map(function(value){ return function(callback){ Transaction.findOne({ where: { id: value.id }}) .then(function(result){ if(!result || result.length === 0){ transactionsToUpdate.push(value); console.log('!result || result.length === 0') } else if(result && result.length === 1){ if(result.hash != value.hash){ transactionsToUpdate.push(value); console.log('result.hash != poenixTrx[i].hash') } } // trigger callback with any result you want callback(null, result) }) .catch(function(err) { console.log(err) // trigger error callback callback(err) }) } }) // async.series will loop through he queryArray, and execute each function one by one until they are all completed or an error is thrown. // for additional information see https://github.com/caolan/async#seriestasks-callback async.series(queryArray, function(err, callback){ // after all your queries are done, execution will be here // resolve the promise with the transactionToUpdate array deferred.resolve(transactionsToUpdate); }) }) .catch(function(err){ throw new Error("Something went wrong getting PhoenixTransaction") }) 

说实话,整个事情有点乱。 特别是承诺/callback混淆可能会在某些时候导致你的问题。 无论如何,你在transactionsToUpdate上使用deferred.resolve,这只是一个数组,所以它立即调用callback函数。

如果你保留这个脚本,而不是_.each像async( https://github.com/caolan/async )来运行你的事务,并且使用它作为callback。

它可能是这样的:

 toexport.getlasttransactions = function(lower,upper,callback){ var transactionsToUpdate = []; /////////////////////////// // set import conditions // /////////////////////////// var lowerbound = (lower) ? lower.format() : moment.utc().subtract(10, 'minutes').format(); var upperbound = (upper) ? upper.format() : moment.utc().format(); /////////////////////////////// // get IDs From Failed syncs // /////////////////////////////// FailedSync.find({ limit: 100 }) .then(function(res){ var FailedIDs = []; _.each(res, function(value,index){ FailedIDs.push(value.transaction_id); }); // build condition var queryCondition = { where: { updated_at: { between: [lowerbound,upperbound] } }, limit: 3 }; if(FailedIDs.length > 0){ queryCondition = { where: Sequelize.and({ updated_at: { between: [lowerbound,upperbound] } }, Sequelize.or( { id: FailedIDs } )) } } ////////////////////////////// // get Phoenix Transactions // ////////////////////////////// PhoenixTransaction .findAll(queryCondition) .then(function(poenixTrx){ async.each(poenixTrx, function(value, next){ Transaction.findOne({ where: { id: value.id }}) .then(function(result){ if(!result || result.length === 0){ transactionsToUpdate.push(value); console.log('!result || result.length === 0') } else if(result && result.length === 1){ if(result.hash != value.hash){ transactionsToUpdate.push(value); console.log('result.hash != poenixTrx[i].hash') } } next(); }) .catch(function(err) { console.log(err) }) }, function(err) { //Return the array transactionsToUpdate in your callback for further use return callback(err, transactionsToUpdate); }); }) .catch(function(err){ throw new Error("Something went wrong getting PhoenixTransaction") }) }) } 

这将是一个callback的方式。 但是你需要把你的想法放在你想要使用的方式:callback或承诺。 不要一起使用(如:如果你的方法期望callback它不应该返回一个承诺,或者如果它返回一个承诺,它不应该期望callback)。

另外,如果你使用callback,你不想抛出错误,你只需调用callback,并在callback中给出错误 – 谁使用你的方法可以检查callback中的错误并处理它。

希望这对你有意义,我知道整个callback和承诺的事情是有点奇怪,如果你来自像PHP一样,它需要一些习惯:)

感谢您解释的差异。 我认为使用承诺是前进的方向,因为它使代码看起来更好,避免了这个“callback地狱”。

例如:

 PhoenixSyncTransactions.getlasttransactions(lastTimeSynced,null) .then(function(res){ return PersistTransaction.prepareTransactions(res).then(function(preparedTrx){ return preparedTrx; }) }).then(function(preparedTrx){ return PersistTransaction.persistToDB(preparedTrx).then(function(Processes){ return Processes; }) }) .then(function(Processes){ return PersistTransaction.checkIfMultiProcess(Processes).then(function(result){ return result; }) }) .then(function(result){ console.log('All jobs done'); }) 

整个代码更容易阅读。