$ .get in a循环:为什么函数在递增后执行?

时间:2013-04-27 15:56:12

标签: javascript jquery loops get

我是Javascript的初学者,我觉得我对$ .get jQuery有问题。

通常,您可以将其分配给将在正确检索数据后执行的函数。

但是如果我将$ .get放在一个循环中,即使尚未检索到数据,循环也会继续执行,这就是我的问题。

这是我的代码(这是GreaseMonkey):

var1 = document.getElementsByClassName("some_class");
i = 0;

while (i < var1.length) {
    url = var1[i].getElementsByTagName("some_tag")[0].href;

    $.get(url, function(data) {
        if (data.contains("some_string")) {
            alert(i);
        }
    });
i++;
}

这里,如果它应该为例子返回1,则警告返回var1.length事件。

我尝试在url声明之后发出警告(i),我知道i ++在我的$ .get中的函数之前完成。

这肯定是一个微不足道的问题,但我无法理解不能实现这一目标的逻辑。

3 个答案:

答案 0 :(得分:2)

包裹您的$.get功能:

(function(i) {
    $.get(url, function(data) {
        if (data.contains("some_string")) {
            alert(i);
        }
    });
})(i);

立即调用的函数表达式导致外部作用域中的当前值i通过函数的参数i绑定(然后隐藏外部变量)。如果您愿意,请为函数参数指定一个不同的名称。

请注意,这只能解决您实际说明的问题,即循环变量的增量与回调无关。如果您希望确保AJAX请求一次运行一个,那么还有其他解决方案,例如:

var els = document.getElementsByClassName("some_class");
var i = 0;

(function loop() {
    if (i < els.length) {
        var el = els[i];
        var url = el.getElementsByTagName("some_tag")[0].href;
        $.get(url).done(function(data) {
            if (data.contains("some_string")) {
                alert(i);
            }
            i++;
        }, loop);   // .done(f1, f2) - see below
     }
})();

.done()调用的格式为.done(callback, loop),将按顺序调用这两个函数。因此,i++行始终首先发生,然后它安排loop伪递归调用以处理下一个元素。

答案 1 :(得分:1)

由于您正在使用jQuery,因此可以简化代码:

$('.some_class').each( function( i, element ) {
    var url = $(element).find('some_tag')[0].href;
    $.get( url, function( data ) {
        if( data.contains("some_string") ) {
            alert( i );
        }
    });
});

原始代码的更改为:

  1. jQuery调用而不是getElementsBy*函数。
  2. 循环的jQuery .each()
  3. 在需要的地方添加了缺失的var。 (在任何版本的代码中都非常重要!)
  4. 请注意,使用.each()会自动提供与另一个答案中的立即调用的函数表达式(IIFE)相同的效果,但不会产生额外的复杂性。这是因为.each()总是使用回调函数,并且为循环的每次迭代创建了唯一保留i变量(以及element)所需的闭包。

    如果您使用普通whilefor循环,并且仍然不需要IIFE,也可以执行此操作。相反,只需在循环中调用一个函数。这样写的代码就是:

    var $elements = $('.some_class');
    for( var i = 0;  i < $elements.length;  i++ ) {
        checkElement( i, $elements[i] );
    }
    
    function checkElement( i, element ) {
        var url = $(element).find('some_tag')[0].href;
        $.get( url, function( data ) {
            if( data.contains("some_string") ) {
                alert( i );
            }
        });
    }
    

    如您所见,checkElement函数与.each()回调函数相同。实际上,.each()只是为您运行一个类似的for循环,并以与此代码完全相同的方式调用回调。此外,for循环比while循环更具可读性,因为它将所有循环变量操作放在一个位置。 (如果您不熟悉for循环语法,最初可能看起来较少可读,但是一旦您习惯了它,您可能会发现您更喜欢{{1} }循环。)

    通常,当试图在循环中间使用IIFE时,请尝试将该代码分解为完全独立的函数。在许多情况下,它会导致更易读的代码。

答案 2 :(得分:0)

Here's a little demo让您进一步调查。

$("#output").empty();

var startTime = new Date().getTime();

// try experimenting with async = true/false and the delay
// don't set async to false with too big a delay,
// and too high a count,
// or you could hang your browser for a while!
// When async==false, you will see each callback respond in order, followed by "Loop finished".
// When async==true, you could see anything, in any order.
var async = true;
var delay = 1;
var count = 5;

function createClosure(i) {
    // return a function that can 'see' i.
    // and i's remains pinned within this closure
    return function (resp) {
        var duration = new Date().getTime() - startTime;
        $("#output").append("\n" + i + " returned: " + resp + " after " + duration + "ms");
    };
}

for (var i = 0; i < count; i++) {
    // jsfiddle url and params
    var url = "/echo/html/";
    var data = {
        html: "hello " + i,
        delay: delay
    };

    $.ajax(url, {
        type: "post",
        data: data,
        async: async
    }).then(createClosure(i));
}

var duration = new Date().getTime() - startTime;
$("#output").append("\n" + "Loop finished after " + duration + "ms");

示例async=true输出:

Loop finished after 7ms
0 returned: hello 0 after 1114ms
1 returned: hello 1 after 1196ms
2 returned: hello 2 after 1199ms
4 returned: hello 4 after 1223ms
3 returned: hello 3 after 1225ms

示例async=false输出(并且浏览器挂起5558ms!):

0 returned: hello 0 after 1113ms
1 returned: hello 1 after 2224ms
2 returned: hello 2 after 3329ms
3 returned: hello 3 after 4444ms
4 returned: hello 4 after 5558ms
Loop finished after 5558ms