2015-02-12 35 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上回答了幾十次。你爲什麼會想象在異步調用已經完成之前數組會被填充?回調正在異步執行 - 這意味着「將來某個時候」。除非您有時間機器,否則您將無法訪問將在將來某個時間不會設置的變量。 – 2015-02-12 02:49:50

+0

感謝torazaburo,正如我所說的,我知道爲什麼......問題是解決這個問題的最優雅的方法。 – 2015-02-12 03:07:29

回答

0

async.*的呼叫替換您的for循環;在這種情況下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] */ });