【问题标题】:Variable scope. Use a closure?可变范围。使用闭包?
【发布时间】:2011-11-17 21:37:03
【问题描述】:

我正在尝试获取我的 Facebook 照片的所有 URL。 我首先使用专辑 ID 加载“专辑”数组。 然后我遍历相册并使用照片 URL 加载“图片”数组。 (我在 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;

        });
    }

【问题讨论】:

  • 问题不在于变量范围,而是 ajax 调用是异步的(它们也应该是异步的)。自 SO 诞生以来,每天都以一种或另一种形式一次又一次地提出这个问题。排序的答案是你不能/不应该return 来自外部函数的任何东西。如果您有更多依赖于pictures 的代码,请在FB.api 回调中使用回调。
  • 我需要返回一个数组,其中包含所有相册中的所有照片。我将如何在第二个嵌套 FB.api 调用中的回调中执行此操作?我必须等到整个“图片”数组都填满后才能返回。
  • @JMan 您没有在回调中返回值,因为没有人可以将其返回(请记住,对 getMyPhotos 的调用已经退出)。您使用回调通知任何对“嘿,这是您请求的数据”感兴趣的人
  • 就像我说的:你不能returnpictures数组。如果有代码依赖于它被填充,它需要在@987654327中执行@回调。

标签: javascript ajax scope closures


【解决方案1】:

您在程序上考虑您的问题。但是,只要您处理异步请求,此逻辑就会失败。我希望您最初尝试做的事情看起来像这样:

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

但是,这不起作用,因为 'pictures' 的值实际上是未定义的(这是任何没有定义实际返回的函数的默认返回类型——这就是你的 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);

我强烈建议您四处搜寻有关 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,然后等待响应完成,然后才能使用它们。不需要本地存储。

【讨论】:

  • 谢谢,马特,我明白了。但是我的其余代码偶尔会需要访问照片,所以所有这些都需要嵌套在 getMyPhotos() 中?有没有办法摆脱这种嵌套混乱?我正在考虑将照片链接写入 HTML5 localStorage,以便其他代码稍后可以引用它。
  • @JMan 更新了我的答案。很多人避免将数据放在全局命名空间中,但我想说,在您的 JS 开发阶段,这是一个可以接受的折衷方案(更不用说比 localStorage 更容易了)。 localStorage 的一个好处是您不必在每次加载页面时都调用 getMyPhotos,而是从 localStorage 中获取它
【解决方案2】:

正如 cmets 中提到的,异步代码就像加利福尼亚酒店 - 你可以随时查看但你永远不能离开

你有没有注意到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();

【讨论】:

    猜你喜欢
    • 2011-12-07
    • 1970-01-01
    • 1970-01-01
    • 2015-09-27
    • 1970-01-01
    • 1970-01-01
    • 2016-03-07
    • 2011-09-23
    • 1970-01-01
    相关资源
    最近更新 更多