嵌套的jQuery AJAX请求以奇怪的顺序完成

时间:2015-04-23 16:54:13

标签: javascript jquery ajax promise

这是我在my previous question遇到的问题的重述,我认为这个问题受到X,Y问题的影响。我希望这个问题更接近我所经历的问题的核心。有关代码,请参阅Github上的scchange.js

问题

我有一组延迟的jQuery对象,我正在调用$.when。这些是将POST请求发送到修改数据的服务器的函数。 $.when调用包含在$.get回调中。这要求我即将修改的数据字段满足授权要求。这是代码:

$.get('/admin/students/contacts/scchange/phoneTlcForm.html?frn=001' + studentsdcid, function () {
  console.log('get callback reached');
  $.when.apply($j, phoneAjaxCalls).done(function () {
    console.log('phoneAjaxCalls promises resolved');
    returnToStudentContacts();
  });
});

我正在使用phoneAjaxCalls.push(newPhone(tlcPhone,studentsdcid))将ajax调用推送到数组,而newPhone返回一个ajax延迟对象:

function newPhone(tlcPhone, studentsdcid) {
    //Create new phone
    return function() {
        return $j.ajax({
            type: 'POST',
            url: '/admin/changesrecorded.white.html',
            data: tlcPhone
        });    
    };
}

运行此代码时,我会在控制台中看到"获得回调"在之前打印" phoneAjaxCalls承诺已解决",这表明请求按照它们在代码中显示的顺序发送。但是,Chrome的DevTools网络标签似乎表明,在加载phoneTlcForm.html文档之前,phoneAjaxCalls网络请求已完成。

enter image description here

前6个请求都是phoneAjaxCalls个请求,最后一个请求是来自phoneTlcForm.html的{​​{1}},但这应该是第一个!

后端似乎按照网络标签显示的顺序接收这些请求,因为我在对这些$.get请求的回复中收到了授权错误。

需要发生的事情是phoneAjaxCalls的请求应在完成任何phoneTlcForm.html请求之前完成。

背景

我的项目是一个插件,因此我无法修改后端逻辑以简化此过程。在创建或更新数据时,我与之交互的系统具有某些授权要求。后端系统必须"看"在发送修改数据的请求之前,我想要修改的数据字段在HTML页面中呈现。这就是我需要在其他请求之前加载phoneAjaxCalls页面的原因。

1 个答案:

答案 0 :(得分:2)

这里发生的事情是,当您构建phoneAjaxCalls数组时,您会为每个项目调用$j.ajax。每次请求都会在您拨打电话时立即发出 我想当你打电话给$j.when.apply($j, phoneAjaxCalls)时,那里的大部分承诺都已经解决了,并且马上就完成了回调。

让我试着用一些评论来说明:

// previous ajax requests already fired!
$j.get('/admin/students/contacts/scchange/phoneTlcForm.html?frn=001' + studentsdcid, function () {
  console.log('get callback reached');
  // inspect the state of phoneAjaxCalls here
  // they might be resolved already.

  // you want to exec those ajax calls here, not before!
  $j.when.apply($j, phoneAjaxCalls).done(function () {
    console.log('phoneAjaxCalls promises resolved');
    returnToStudentContacts();
  });
});

所以从这里你有不同的方法来解决它。我认为你已经在question you linked得到了很好的回应。

在每个地方你返回$ j.ajax,你可以返回一个包装它的函数,以避免执行。我们以updateEmail function为例:

return function(){
    return $j.ajax({
            url: '/ws/schema/table/' + config.contactsEmailTable + '/' + emailRecordId,
            data: JSON.stringify(emailUpdateData),
            dataType: 'json',
            contentType: "application/json; charset=utf-8",
            type: 'PUT'
        });
 };

包装完所有内容后,你可以这样打电话给他们:

$.get('/admin/students/contacts/scchange/phoneTlcForm.html?frn=001' + studentsdcid, function () {
  var phoneAjaxCallsPromises = $.map(phoneAjaxCalls, function(c){
    return c();
  });
  $.when.apply($j, phoneAjaxCalls).done(function () {
    console.log('phoneAjaxCalls promises resolved');
    returnToStudentContacts();
  });
}); 

这个解决方案感觉像是一个黑客,但理解这个问题可能会让你实现正确的解决方案。