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

Jquery Deferred in each loop with ajax callback

参考, I tried using deferred object in nested ajax loop. However the output is not returning as expected. I have updated my code in fiddle for reference. - https://jsfiddle.net/fewtalks/nvbp26sx/1/

代码:

  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')});

代码包含一个执行循环的主函数。在每个循环中,执行一个子函数(测试)。在子函数(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后测试函数执行完毕。为什么函数没有按顺序执行每个循环。

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

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')
});
<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>

为什么在处理 test2 函数之前记录 'completed'?

你的结果符合预期。

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

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

的两条日志消息之后

记住 $.when() 运行 事情是并行的,所以你传递给它的所有承诺都是同时进行的。

如果您想序列化对 test(i) 的调用,以便下一个调用不会在第一个调用之后发生,那么您需要采取不同的方式。

此外,您在 main() 中使用了 anti-pattern,方法是在不需要创建延迟的地方创建延迟。你可以 return $.when.apply(...)。你不需要将它包装在另一个 deferred 中。


要序列化对 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() 是很自然的,因为我们将一个值传递给我们将下一次迭代链接到的下一次迭代(承诺)。它还 return 也是一个承诺,因此您可以知道整个事情何时完成。