2015-02-13 3 views
1

, исходящий из php-фона, я пытаюсь разгадать этот материал обратного вызова.Sequelize подождать, пока цикл не завершится с обратным вызовом

В принципе, я хочу получить несколько строк, тогда я хотел бы пропустить эти строки и проверить их на другой модели (другой db). Я хочу, чтобы звонок вернулся, пока все они не зациклились и не проверили.

Обратный вызов вызывается до того, как sequelize проведет все результаты.

В принципе, я хочу, чтобы функция была «блокирующей». Что мне нужно изменить?

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;  

} 
+0

Какая версия узла вы используете (или вы используете новый io.js?)? –

+1

Также - http://stackoverflow.com/questions/23803743/what-is-the-deferred-antipattern-and-how-do-i-avoid-it –

ответ

0

спасибо за объяснение различий. Я думаю, что работа с обещаниями - это путь вперед, потому что он делает код более приятным и избегает этого «обратного ада».

Например:

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'); 
}) 

Весь код легче читать.

0

В идеале вы хотите, чтобы либо использовать что-то вроде Bluebird уменьшим с массивом обещаний, но я обеспечить реализацию async.series как его легче понять.

Установите ASync

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") 
     }) 
+1

Sequelize использует обещания bluebird, которые вы можете просто использовать. –

0

Все это немного запутанна, чтобы быть честным. В частности, смешение обещаний/обратного вызова, вероятно, вызовет проблемы в какой-то момент. В любом случае вы используете отложенное. Решите на transactionToUpdate, который является просто массивом, поэтому он сразу же вызывает обратный вызов.

Если вы храните этот скрипт в качестве его использования вместо _.each что-то вроде async (https://github.com/caolan/async), чтобы выполнять транзакции в paralell и использовать это как обратный вызов.

Это может выглядеть следующим образом:

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 или обещания. Не используйте оба вместе (как в: Если ваш метод ожидает обратного вызова, он не должен возвращать обещание или если он возвращает обещание, он не должен ожидать обратного вызова).

Дополнительный, если вы используете обратный вызов, который не хотите бросать ошибки, вы просто вызываете обратный вызов и выдаете ошибку в обратном вызове. Тот, кто использует ваш метод, может проверить ошибку на обратном вызове и обработать ее.

Надежда, что своего рода имеет смысл для вас, я знаю весь обратный вызов и обещает, что это немного странно, если вы из чего-то вроде PHP и требует некоторого привыкания :)

+0

Вы понимаете, что можете просто '.each' с обещаниями bluebird правильно? Также вы не дожидаетесь его окончания. –

+0

Я не знаю Q lib, но не имеет смысла возвращать обещание и вызывать обратный вызов. Я уверен, что есть элегантное решение, использующее обещания, но вы должны полностью избавиться от обратного вызова. И, да, вы правы. Это не имело смысла. Починил это. Не пробовал, хотя ... могу также изменить что-то еще. –

+0

Я не пытаюсь вас подвести, но чтобы дать вам общее представление о том, насколько проще это может быть, вы можете проверить мое решение - это не подавлять ошибки (например, '.catch' в вашем коде) и не игнорировать их - что происходит, например, если один из ранних уровней обещаний терпит неудачу? –

3

У вас есть много шаблонов новые обещания пользователей в вашем коде:

  • Вы используете отложенное, когда вам это не нужно.
  • Вы не используете методы агрегирования обещаний
  • Вы не ждете вещей в соответствующих местах, кроме гнездования.

Обещание представляет собой значение с течением времени.Вы можете использовать обещания и получить доступ к их результатам через then в более поздний момент, а не сразу - обещания Sequelize основаны на bluebird и предлагают богатый 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); 
    }); 
};