2011-11-17 66 views
2

我试图抓住我的Facebook照片的所有网址。 我首先加载专辑ID的“专辑”阵列。 然后,我浏览相册并加载带有照片网址的“图片”阵列。 (我在Chrome的JS调试器中看到了这一点)。变量作用域。使用闭包?

但是,当代码到达最后一条语句(“返回图片”)时,“图片”是空的。

我该如何解决这个问题? 我觉得我应该使用闭包,但不完全确定如何最好地做到这一点。 谢谢。

function getMyPhotos() { 

     FB.api('/me/albums', function(response) { 
      var data = response.data; 
      var albums = []; 
      var link; 
      var pictures = []; 


      // get selected albums id's 
      $.each(data, function(key, value) { 
       if ((value.name == 'Wall Photos')) { 
         albums.push(value.id); 
       } 
      }); 
      console.log('albums'); 
      console.log(albums); 

      // get the photos from those albums 
      $.each(albums, function(key, value) { 
       FB.api('/' + value + '/photos', function(resp) { 
        $.each(resp.data, function(k, val) {  
          link = val.images[3].source; 
          pictures.push(link); 
        }); 
       }); 
      }); 

      console.log('pictures'); 
      console.log(pictures); 
      return pictures; 

     }); 
    } 
+7

问题不是变量作用域,而是ajax调用是异步的(它们应该是)。这个问题已经以某种形式或另一次一次又一次地被询问过,自从SO开始的每一天。排序的答案是你不能从外部函数中返回任何东西。如果您有更多依赖于“图片”的代码,请在FB.api回调中使用回调。 –

+0

我需要返回所有相册中包含所有照片的阵列。我将如何在第二个嵌套FB.api调用中的回调中执行此操作?我必须等到整个“图片”数组填满后再返回。 – JMan

+1

@JMan你不会在回调中返回一个值,因为没有人可以返回它(记住,对getMyPhotos的调用已经退出)。你使用回调来通知谁是有意图的“嘿,这是你请求的数据” – Matt

回答

3

你正在程序上思考你的问题。但是,只要您使用异步请求,此逻辑就会失败。我希望您最初尝试做看起来是这样的:

var pictures = getMyPhotos(); 
for (var i = 0; i < pictures.length; i++) { 
    // do something with each picture 
} 

但是,既然的“照片”的值不工作实际上是不确定的(这是任何函数的默认返回值类型不实际返回定义 - 这是你的getMyPhotos做什么)

相反,你想要做这样的事情:

function getMyPhotos(callback) { 
    FB.api('/me/albums', function (response) { 
     // process respose data to get a list of pictures, as you have already 
     // shown in your example 

     // instead of 'returning' pictures, 
     // we just call the method that should handle the result 
     callback(pictures); 
    }); 
} 

// This is the function that actually does the work with your pictures 
function oncePhotosReceived(pictures){ 
    for (var i = 0; i < pictures.length; i++) { 
     // do something with each picture 
    } 
}; 

// Request the picture data, and give it oncePhotosReceived as a callback. 
// This basically lets you say 'hey, once I get my data back, call this function' 
getMyPhotos(oncePhotosReceived); 

我强烈建议你攒够SO周围更多的问题/关于AJAX回调和异步的JavaScript答案节目。

编辑:

如果你想保持FB API调用的结果得心应手其他代码中使用,你可以返回值设置到窗口中的“全局”变量:

function getMyPhotos(callback) { 
    FB.api('/me/albums', function (response) { 
     // process respose data to get a list of pictures, as you have already 
     // shown in your example 

     // instead of 'returning' pictures, 
     // we just call the method that should handle the result 
     window.pictures = pictures; 
    }); 
} 

现在,您可以在任何需要的地方使用全局变量'pictures'(或明确使用window.pictures)。当然,这种情况是你必须先调用getMyPhotos,然后等待响应完成后才能使用。无需localStorage。

+0

谢谢,马特,我明白了。但是我的其他代码偶尔需要访问这些照片,所以它们都需要嵌套在getMyPhotos()中?有没有办法摆脱这种嵌套混乱?我正在考虑将相片链接写入HTML5 localStorage,以便其他代码稍后可以参考它。 – JMan

+0

@JMan更新了我的答案。很多人都避免将数据放入全局命名空间,但我会说在你的JS开发阶段这是一个可以接受的折衷方案(更不用说比localStorage更容易)。 localStorage会给你带来的一个好处是你不必在每次加载页面时进行getMyPhotos调用,而是从localStorage中获取它 – Matt

0

正如在评论中提到的,异步代码就像加州酒店 - 你可以检查任何你喜欢的时间,但你永远不会离开

你注意到FB.api怎么没有返回值

//This is NOT how it works: 
var result = FB.api('me/albums') 

而是接收的延续功能上它通过它的结果?

FB.api('me/albums', function(result){ 

原来你需要有一个类似的安排您的getMyPhotos功能:

function getMyPhotos(onPhotos){ 
    //fetches the photos and calls onPhotos with the 
    // result when done 

    FB.api('my/pictures', function(response){ 

     var pictures = //yada yada 

     onPhotos(pictures); 
    }); 
} 

当然,延续传递风格是会传染的,所以你现在需要调用

getMyPhotos(function(pictures){ 

而不是

var pictures = getMyPhotos();