(仍然)对javascript闭包,ajax和返回值更加困惑

时间:2012-06-28 16:02:48

标签: javascript jquery loops closures

我正在尝试使用JQuery .get()方法和javascript for循环来处理来自外部文件的一些数据。我已经阅读了关于stackoverflow上的回调的闭包和返回值几个小时,我仍然感到困惑,为什么这不起作用。

关于内部回调函数,变量 headers countryData 的范围是否全局?它们在回调函数中按预期分配值,但是一旦完成,我该如何访问它们?可能是一个没有使用alert()函数的例子?

function processData(inCSV){
    var headers;  
    var countryData = [];
    $.get(inCSV, function(data) {
        var lines = data.split('\r\n');
        for(var i=0;i<=lines.length-1;i++){
            var lineData = lines[i].split(',');
            if(i != 0){
                countryData[lineData[1]] = lineData.slice(2,lineData.length);
            } else {
                headers = lineData.slice(2,lineData.length);
            }
        }
        console.log('inside',headers);  // output 'inside ["1971", "1972", "1973" ...'
        console.log('inside',countryData['Brazil']);  // output 'inside ["56.4", "54.6", ..'
    });
    console.log('outside',headers);  // output 'outside undefined' ...!?
    console.log('inside',countryData['Brazil']);    // output 'outside undefined' ...!?
}   

3 个答案:

答案 0 :(得分:3)

问题不在于闭包,问题在于异步功能。 $ .get()连接到服务器,并在服务器返回答案时运行其回调函数。但是,一旦请求发送,$ .get()就会完成,而不是在返回响应时。因此,在执行回调函数之前,最后两个console.log()行正在运行。

一旦执行了回调函数,您只能访问headerscountryData变量,并且您知道发生的唯一位置是回调函数本身。或者它调用的其他代码。

答案 1 :(得分:0)

$.get异步,这意味着脚本的其余部分不会等待它完成。如果您需要比成功回调提供的控制更多的控制,您可以使用jQuery.Deferred类(docs)来缓解此问题,或者您可以进行同步调用(意味着脚本的其余部分等待它在执行之前完成。)

同步AJAX调用

您需要使用$.ajaxdocs),只需传入async:false

$.ajax({
  url: inCSV,
  async: false,
  success: function() { /* ... */ }
});

// code here will not execute until the ajax call above is complete

延迟对象

function processData(inCSV) {
    var deferred = jQuery.Deferred();
    $.ajax({
        url: inCSV, 
        success: function(data){
            // do stuff
            deferred.resolve([data]);
        },
        error: function() {
            deferred.reject();
        }
    });
    return deferred;
}

processingData = processData(inCSV);

// any code that doesn't care about the processing results can go here

// code that relies on headers or countryData must go in a block like this
// you can add as many "done" blocks as you like
processingData.done(function(data){
    // mess with data here
});

答案 2 :(得分:0)

这不是封闭问题。只是代码行没有按照它们的编写顺序执行。

这是一个基本的事件编程问题:进程的结束位于代码的中间。一旦你意识到这一点,这不是一个大问题。您只需在正确的位置写下流程的结尾。

在您的情况下,事情按此顺序发生:

步骤1.使用以下代码声明状态变量:

var headers;  
var countryData = [];

步骤2.使用此代码调用服务器

$.get(inCSV, <CALLBACK>)

此时回调中的内容根本不重要。在服务器响应返回之前,它不会被执行。

步骤3.您将状态变量与此代码一起使用

console.log('outside',headers);  // output 'outside undefined' ...!?
console.log('inside',countryData['Brazil']);    // output 'outside undefined' ...!?

它们是未定义的,这是完全可以预期的,因为没有代码初始化它们。

步骤4.响应从服务器返回:

    var lines = data.split('\r\n');
    for(var i=0;i<=lines.length-1;i++){
        var lineData = lines[i].split(',');
        if(i != 0){
            countryData[lineData[1]] = lineData.slice(2,lineData.length);
        } else {
            headers = lineData.slice(2,lineData.length);
        }
    }
    console.log('inside',headers);  // output 'inside ["1971", "1972", "1973" ...'
    console.log('inside',countryData['Brazil']);  // output 'inside ["56.4", "54.6", ..'