Jquery在每个循环中使用ajax回调延迟

时间:2016-07-19 20:58:58

标签: jquery-deferred

参考https://stackoverflow.com/a/13951699/929894,我尝试在嵌套的ajax循环中使用延迟对象。但是输出没有按预期返回。我已经用小提琴更新了我的代码以供参考。 - https://jsfiddle.net/fewtalks/nvbp26sx/1/

CODE:

  function main() {
    var def = $.Deferred();
    var requests = [];
    for (var i = 0; i < 2; i++) {
        requests.push(test(i));
    }
    $.when.apply($, requests).then(function() {
        def.resolve();
    });

    return def.promise();
}

function test(x){
    var def = $.Deferred();
  test1(x).done(function(){
                setTimeout(function(){ console.log('processed test item', x); def.resolve();}, 1000);
          });
  return def.promise();
}

function test1(items){
    var _d = $.Deferred();
  setTimeout(function(){ 
    console.log('processed test1 item', items); 
    _d.resolve();
  });
  return _d.promise();
}

main().done(function(){ console.log('completed')});

代码包含一个执行循环的main函数。在每个循环上,执行子功能(测试)。在子函数(test)内部调用另一个函数(test1)。子函数test和test1都有AJAX调用声明。对于AJAX调用,我使用了setTimeout属性。我期待像

这样的输出
processed test1 item0
processed test item0
processed test1 item1
processed test item0
completed

对于每个循环,我希望函数执行为Test1()然后test();但是我得到的结果是

processed test1 item 0
 processed test1 item 1
processed test item 0
processed test item 1
completed

执行test1后,执行完全测试功能。为什么函数没有按顺序执行每个循环。

另一个测试运行的更新代码

&#13;
&#13;
function main(items) {
    var items = items;
    return items.reduce(function (p, index) {
        return p.then(function () {
            return test(index);
        });
    }, $.Deferred().resolve());
}

function test(x) {
    var def = $.Deferred();
    test1(x).done(function () {
        setTimeout(function () {
            log('processed test item', x);
            def.resolve();
        }, 1000);
    });
    return def.promise();
}

function test1(items) {
    var _d = $.Deferred();
    setTimeout(function () {
        log('processed test1 item', items);
        _d.resolve();
    });
    return _d.promise();
}

var items = [0, 1];
function test2(x) {
    var _d = $.Deferred();
    setTimeout(function () {
        log('processed test2 item',x);
        _d.resolve();
    });
    return _d.promise();
}

main([1,2]).done(function(data){test2(items);}).then(function () {
    log('completed')
});
&#13;
<script src="https://dl.dropboxusercontent.com/u/7909102/log.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
&#13;
&#13;
&#13;

为什么&#39;已完成&#39;在处理test2函数之前是否已记录?

1 个答案:

答案 0 :(得分:2)

您的结果符合预期。

您的for循环同步运行完成并运行test()两次。

test()然后立即调用test1(),所以你看到的第一件事是test1()可以运行两次。然后,在每个test1()完成其承诺后,它会为您的test()日志消息设置计时器。很自然地,来自test()的两条日志消息来自test1()的两条日志消息。

请记住,$.when()并行运行,因此您传递的所有承诺都会同时进行。

如果您要将您的来电序列化为test(i),那么下一个号码要么在第一个号码之后才会发生,那么您需要采取不同的做法。

此外,您在main()中使用anti-pattern创建了延迟,而不需要创建延迟。您只需返回$.when.apply(...)即可。你不需要把它换成另一个延期的。

要将您的来电序列化为test(i)以获取您想要的输出类型,您可以执行以下操作:

function main() {
    var items = [0, 1];
    return items.reduce(function(p, index) {
        return p.then(function() {
            return test(index);
        });
    }, $.Deferred().resolve());
}

生成所需输出的工作演示:https://jsfiddle.net/jfriend00/hfjvjdcL/

.reduce()设计模式经常用于串行迭代数组,调用一些异步操作并等待它完成,然后再调用数组中下一项的下一个异步操作。使用.reduce()是很自然的,因为我们将一个值带到下一次迭代(一个承诺),我们将下一次迭代链接到。它也会返回一个承诺,所以你可以知道整个事情何时完成。