【问题标题】:Deferred promise AJAX loop then() never happens延迟承诺 AJAX 循环 then() 永远不会发生
【发布时间】:2014-06-11 14:01:05
【问题描述】:

我正在尝试在 AJAX 循环中使用延迟承诺,因为使用 async: false 会锁定页面。我已经探索过其他类似的用途,但我不明白为什么 $.when().apply().then() 永远不会执行。

    var userData = {};  
    userData.user1 = {"creds":{"userid":"user1@test.com","password":"user1pass","country":"us"}};
    userData.user2 = {"creds":{"userid":"user2@test.com","password":"user2pass","country":"mx"}};
    userData.user3 = {"creds":{"userid":"user3@test.com","password":"user3pass","country":"ca"}};
    function loginUser(credentials){
        var def = new $.Deferred();
        requests = [];
        $.each(credentials, function(k, v){
            var promise = $.ajax({
                url : '/path/to/my/uri', 
                type : 'post',
                contentType : 'application/json',
                data : JSON.stringify(v)
            });
            requests.push(promise); 
        });
        console.log(requests);
        // check if all ajax calls have finished
        $.when.apply($, requests).then(function() {
            // this never happens
            def.resolve();
            return def.promise();
            console.log('the request is done');
        });
    }
    loginUser(userData);


//updated snippet
var userData = {};  
userData.user1 = {"creds":{"userid":"user1@test.com","password":"user1pass","country":"us"}};
userData.user2 = {"creds":{"userid":"user2@test.com","password":"user2pass","country":"mx"}};
userData.user3 = {"creds":{"userid":"user3@test.com","password":"user3pass","country":"ca"}};
function loginUser(credentials){
    var requests = $.map(credentials, function(k, v){
    return $.ajax({
            url : '/path/to/my/uri', 
            type : 'post',
            contentType : 'application/json',
            data : JSON.stringify(k)
        });
    });
    console.log(requests);
    // return a promise that will resolve when all ajax calls are done
    return $.when.apply($, requests);
}

loginUser(userData).done(function() {
// all logins completed here
console.log('inside done');
}).fail(function() {
// will be called if any login failed
console.log('inside fail');
});

// current syncronous functionality
function loadUrls(credentials){
    $.each(credentials, function(k, v){
        $.ajax({
            url : '/path/to/my/uri', 
            type : 'post',
            contentType : 'application/json',
            success: function(data, textStatus, jqXHR) {
                $('body').append('<a target="_blank" href="'+data.userInfo+'">'+data.userName+' '+k+'</a><br />');
            },
            data : JSON.stringify(v),
            error : function(jqXHR, textStatus, errorThrown){
                return;                 
            },
            async : false      
        });
    });
}

【问题讨论】:

  • 您确定所有请求都已成功发送和处理吗?尝试将then 更改为done 或附加fail 处理程序以及then
  • 如果您想在其他地方使用它,您应该在 loginUser 的末尾返回 def.promise(),而不是 $.when,但这不应阻止日志语句的发生。
  • 不,但是return 调用将...
  • @orzechowskid Doh!你完全正确。

标签: jquery ajax promise deferred


【解决方案1】:

您的日志声明在这里:

    $.when.apply($, requests).then(function() {
        // this never happens
        def.resolve();
        return def.promise();
        console.log('the request is done');
    });

永远不会发生,因为它就在 return 语句之后。

也许您的意思是从loginUser() 返回def.promise()。但是,唉,根本没有理由创建这个额外的延迟。 $.when() 已经返回了一个可以以这种方式使用的承诺。


这就是我认为你可能真正想要的:

var userData = {};  
userData.user1 = {"creds":{"userid":"user1@test.com","password":"user1pass","country":"us"}};
userData.user2 = {"creds":{"userid":"user2@test.com","password":"user2pass","country":"mx"}};
userData.user3 = {"creds":{"userid":"user3@test.com","password":"user3pass","country":"ca"}};
function loginUser(credentials){
    var requests = [];
    $.each(credentials, function(k, v){
        var promise = $.ajax({
            url : '/path/to/my/uri', 
            type : 'post',
            contentType : 'application/json',
            data : JSON.stringify(v)
        });
        requests.push(promise); 
    });
    console.log(requests);
    // return a promise that will resolve when all ajax calls are done
    return $.when.apply($, requests);
}

loginUser(userData).done(function() {
   // all logins completed here
}).fail(function() {
   // will be called if any login failed
});

或者,正如 Benjamin Gruenbaum 建议的那样,您可以使用 $.map() 而不是 $.each() 来稍微缩短代码。

var userData = {};  
userData.user1 = {"creds":{"userid":"user1@test.com","password":"user1pass","country":"us"}};
userData.user2 = {"creds":{"userid":"user2@test.com","password":"user2pass","country":"mx"}};
userData.user3 = {"creds":{"userid":"user3@test.com","password":"user3pass","country":"ca"}};
function loginUser(credentials){
    var requests = $.map(credentials, function(k, v){
        return $.ajax({
            url : '/path/to/my/uri', 
            type : 'post',
            contentType : 'application/json',
            data : JSON.stringify(v)
        });
    });
    console.log(requests);
    // return a promise that will resolve when all ajax calls are done
    return $.when.apply($, requests);
}

loginUser(userData).done(function() {
   // all logins completed here
}).fail(function() {
   // will be called if any login failed
});

如果您说的是您的 $.when.done() 处理程序从未被调用(即使您将 console.log() 语句放在将被执行的位置),那么发生的唯一方法是如果您的 ajax 之一呼叫未成功完成。除了.done() 处理程序之外,您还可以通过添加.fail() 处理程序来观察。


如果您真正想要对多次登录做的只是找出至少一个登录是否成功(即使其他登录失败),那么您将需要不同的逻辑来检测这种情况,因为$.when()只会告诉您是否所有承诺都已解决或是否有任何承诺被拒绝。它不会告诉你是否至少有一个被接受。为此,我们必须知道如果登录被拒绝,ajax 调用中会发生什么。 ajax调用是否依然成功,但结果显示登录失败?如果是这样,那么您必须检查传递给 .done() 处理程序的数据,以查看是否至少有一次登录成功。

【讨论】:

  • 我会将这个 $.each 重构为 .map 所以 return $.when.apply($.map(credentials, function( k,v){ return $.ajax(... 除了解决 OP 在承诺方面遇到的问题的非常好的答案。了解延迟反模式并解决 OP 遇到的实际问题。
  • @BenjaminGruenbaum - 我添加了您的$.map() 建议作为进一步优化。
  • 抱歉回复晚了,发帖后遇到了一些问题。因此,console.log() 上面的 return 语句显然是错误的。我想我没有意识到如果任何请求失败就不会发生 done() 。当 async 设置为 false 时,我不明白为什么请求会完成,尽管速度很慢,但是当尝试使用 promise() 方法保留状态时,一些请求会失败。两种情况下请求的状态不应该相同,只是在处理一组异步承诺对象时更快的页面加载,应该具有与异步为假时相同的状态?
  • @neridaj - 将事情从异步更改为阻塞通常会更改代码中的事件顺序,因此代码通常必须以一种或另一种方式编写。请为异步 Ajax 设计。您的用户会非常感谢您,因为他们的浏览器页面不会锁定,并让他们认为网站可能在 ajax 调用期间挂起(特别是在他们的连接或您的服务器出现任何缓慢的情况下)。
  • 这仍然不起作用。即使所有请求都是 200 done() 也永远不会被调用。如果一个请求得到 503,这是否意味着所有 200 个请求都是垃圾?有没有更好的方法来循环 ajax 调用,因为这不起作用。`inside fail XHR Loaded (authenticate - 200 OK - 351.0608673095703ms - 311B) VM8380:3 XHR Loaded (authenticate - 200 OK - 450.9708881378174ms - 311B) VM8386:3 XHR 已加载(验证 - 200 OK - 483.80398750305176ms - 311B)'
猜你喜欢
  • 2021-03-04
  • 2013-04-27
  • 2011-07-23
  • 2016-05-06
  • 2015-03-08
  • 1970-01-01
  • 2019-11-29
  • 2021-11-04
相关资源
最近更新 更多