2015-02-12 10 views
-1

У меня есть цикл, который мне нужно запустить внутри обратного вызова, к сожалению доступ к массиву вне обратного вызова оставляет меня с пустым массивом. Я знаю, почему это происходит, но я хочу знать лучшее решение для решения этой проблемы.Нажатие на массив внутри цикла внутри функции обратного вызова

Gallery.prototype.getGallery = function(cb) { 
self = this; 
var cos = new pb.CustomObjectService(); 
var ms = new pb.MediaService(); 

var s = []; 

cos.loadTypeByName('Gallery Image', function(err, gallery){ 

    cos.findByType(gallery._id.toString(), function(err, rpy){ 

     for(var i = 0; i < rpy.length; i++){ 
      ms.loadById(rpy[i].Image, function(e,r){ 
       s.push(r.location); 
       console.log(r.location); /* <-- logs expected data */ 
      });  
     } 
     console.log(s[0]); /* <-- this is undefined */ 
    }); 
}); 
}; 
+1

Я не downvoter, но это вопрос был задан и ответил десятки раз на SO. Почему бы вам предположить, что массив будет заполнен до того, как асинхронный вызов завершится? Обратный вызов выполняется асинхронно - это означает «когда-нибудь в будущем». Если у вас нет машины времени, вы не можете получить доступ к переменным, которые не будут установлены до определенного времени в будущем. –

+0

Спасибо torazaburo, как я уже сказал, я знаю, почему ... ВОПРОС - это самый элегантный способ справиться с этим. –

ответ

0

Заменить for цикл с вызовом async.*; в этом случае async.map похоже правый. Пропустить обратный вызов до async.map; он будет вызываться, когда будут выполнены все индивидуальные вызовы ms.loadById с массивом результатов.

async.map(
    rpy, 
    function(elt, callback) { 
     ms.loadById(elt.Image, callback); 
    }, 
    function(err, data) { 
     // comes here after all individual async calls have completed 
     // check errors; array of results is in data 
    } 
); 

Если вы хотите идти в мир обещает, затем обернуть вызовы ms.loadById в обещании. Вот сводная версия, но различные версии того, что обычно называется promisify, также есть.

function loadByIdPromise(elt) { 
    return new Promise(function(resolve, reject) { 
     ms.loadById(elt.image, function(err, data) { 
      if (err) return reject(err); 
      resolve(data); 
     }); 
    }); 
} 

Затем сделать Promise.all на результирующих обещаниях:

Promise.all(rpy.map(loadByIdPromise)) 
    .then(function(data) { 
     // comes here when all individual async calls complete successfully 
     // data is your array of results 
    }); 

Используя стиль обещания, весь ваш код будет выглядеть следующим образом:

loadTypeByNamePromise('Gallery Image') . 
    then(function(gallery) { return findByTypePromise(gallery._id.toString(); }) . 
    then(function(rpy)  { return Promise.all(rpy.map(loadByIdPromise)); }) . 
    then(function(results) { /* do something with [results] */ }); 
Смежные вопросы