【问题标题】:Jquery Deferred in each loop with ajax callback使用ajax回调在每个循环中延迟Jquery
【发布时间】:2016-07-19 20:58:57
【问题描述】:

参考https://stackoverflow.com/a/13951699/929894,我尝试在嵌套的ajax 循环中使用延迟对象。但是输出没有按预期返回。我已经在小提琴中更新了我的代码以供参考。 -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 函数之前记录“完成”?

【问题讨论】:

  • 请将您的代码直接粘贴到您的问题中。此处不允许仅依赖外部代码引用的问题,并且外部引用往往会更改或消失,从而使该问题无法作为将来对其他人的参考。

标签: jquery-deferred


【解决方案1】:

您的结果符合预期。

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

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

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

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

此外,您在main() 中使用anti-pattern,方法是在不需要创建延迟的地方创建延迟。你可以直接返回$.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() 是很自然的,因为我们将一个值传递到下一次迭代(一个承诺),我们将下一次迭代链接到该迭代。它还返回一个承诺,所以你可以知道整个事情什么时候完成。

【讨论】:

  • 我试过你的样品小提琴,它按预期工作。但是,如果我尝试在 main 函数之后添加一个新的 ajax 调用。它没有按预期工作。jsfiddle.net/fewtalks/43bbzbeh。日志应在“完成”之前打印“已处理的 test2 项”
  • @fewtalks - 与往常一样,我可以帮助我提供我能看到的代码。无法帮助您未显示的代码。如果您显示您遇到问题的确切代码,我可以看看。
  • 更新了问题部分的新代码。用 console.log() 替换日志
  • @fewtalks - 停止使用 jQuery 的脑残 .done() 并使用 .then(),它按照承诺规范正确链接(jQuery 的 1.x 和 2.x 与 @987654344 有其他非标准问题@,但至少它正确链接)。见jsfiddle.net/jfriend00/7666yrg0
  • 将参数传递给延迟函数未按预期工作。更新了有问题的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-27
  • 2013-04-27
  • 2013-01-25
  • 1970-01-01
  • 2020-07-08
相关资源
最近更新 更多