Javascript 如何处理自身被异步触发的异步ajax调用

Javascript 如何处理自身被异步触发的异步ajax调用,javascript,jquery,ajax,asynchronous,Javascript,Jquery,Ajax,Asynchronous,我有一个问题: 用户单击按钮a,然后通过ajax调用创建一些内容。该调用返回一些数据 稍后,用户(在输入中输入一些文本)将单击按钮B。这将启动另一个ajax调用。现在,这个需要第一个请求返回的数据 当第一次呼叫足够快时,这没有问题。但是,我必须假设这可能需要一段时间,因此当用户单击按钮B时,第一次呼叫可能无法完成 我不想禁用按钮B,只要第一个按钮持续。即使第一个ajax调用尚未完成,用户也应该单击按钮B。因此,我似乎需要以某种方式将呼叫链接/排队 我怎么能这样做?如何通过更长的调用序列来实现这一

我有一个问题:

用户单击按钮a,然后通过ajax调用创建一些内容。该调用返回一些数据

稍后,用户(在输入中输入一些文本)将单击按钮B。这将启动另一个ajax调用。现在,这个需要第一个请求返回的数据

当第一次呼叫足够快时,这没有问题。但是,我必须假设这可能需要一段时间,因此当用户单击按钮B时,第一次呼叫可能无法完成

我不想禁用按钮B,只要第一个按钮持续。即使第一个ajax调用尚未完成,用户也应该单击按钮B。因此,我似乎需要以某种方式将呼叫链接/排队

我怎么能这样做?如何通过更长的调用序列来实现这一点。这个场景有名字吗?已经有模式了吗?还是我只是瞎了

基本示例代码:

$('button.a').click(function () {
    $.post('some/url/for/action/a', {})
        .done(function (returnedData) {

        })
});

$('button.b').click(function () {
    // now I need something from returnedData, which may not be available yet
    $.post('some/url/for/action/b', {something: returnedData.something})
        .done(function () {

        })
});
注:最终目的是加快流程以改进用户体验


(请随意改进问题的标题。我无法表达得更好。)

您实际上想要进行同步调用。我建议您使用
$.ajax
而不是
$.post
,因为它更易于定制

您的按钮B功能如下所示:

$.ajax({
  type: 'POST',
  url: 'some/url/for/action/b',
  data: data,
  success: success,
  async:false
});

async:false
选项使您的请求同步。

如果您可以使用最新版本的ECMAScript规范,那么您可能需要考虑使用。有一个非常好的方法,但它们基本上是我们可以用来等待异步完成的对象

如果将第一个ajax调用封装在Promise对象中,那么稍后(在您的情况下,在用户单击按钮B之后)可以返回到它,并查看它是否已经完成。如果有,
then()
您可以做一些响应

var promiseA; // this variable will hold our Promise object

function postActionA() {
  return new Promise(function(resolve, reject) {
    $.post('some/url/for/action/a', {}).done(resolve); // this is the magic
    // when the ajax call completes, it calls the 'resolve' function
  });
}

$('button.a').click(function () {
  promiseA = postActionA();
  // by assigning our promise object to a variable
  // we can inspect its state later on
  promiseA.then(function(returnedData) {
    // do stuff with returnedData here if you like
  });
});

$('button.b').click(function () {
  // assuming that promiseA was assigned earlier
  // we can wait for it to resolve, then make our next ajax call
  promiseA.then(function (returnedData) {
    $.post('some/url/for/action/b', {something: returnedData.something})
      .done(function () {
      // do something after the second ajax call completes
      });
  });
});
如果您不能使用最新版本的ECMAScript,而希望使用jQuery,那么我们可以使用jQuery的延迟对象来执行类似的操作

var deferredA; // this variable will hold our Deferred object

$('button.a').click(function () {
  deferredA = $.post('some/url/for/action/a', {});
  // by assigning our deferred object to a variable
  // we can inspect its state later on
  deferredA.done(function(returnedData) {
    // do stuff with returnedData here if you like
  });
});

$('button.b').click(function () {
  // assuming that deferredA was assigned earlier
  // we can wait for it to resolve, then make our next ajax call
  deferredA.done(function (returnedData) {
    $.post('some/url/for/action/b', {something: returnedData.something})
      .done(function () {
      // do something after the second ajax call completes
      });
  });
});

我的想法是基于在单击按钮时将ajax调用排队。然后从队列中拾取呼叫并触发

通过这种方式,您可以单击随机序列(如A、B、A、A、B、A、B、B),随后的每个调用都将等待前面的调用

通过全局变量将数据从一个调用传递到另一个调用

守则:

var queue = [];
var processing = false;
var dataFromLastCall = {}; //data from previous call

function register(callParams){
  queue.push(callParams);
  if(processing == false){
    process(queue);
  }
}

function process(){
  processing = true;
  var call = queue.shift();

  $.when(jQuery.ajax(call)).then(function(){
    if(queue.length > 0){
      process(); //get another
    }else{
      processing = false; //all done
    }
  });
}

$("button.a").click(function(e){
  e.preventDefault();
  register({
    type: 'POST',
    url: 'some/url/for/action/a',
    data: dataFromLastCall,
    success: function(newData){
      dataFromLastCall = newData;
    }
  });
});

$("button.b").click(function(e){
  e.preventDefault();
  register({
    type: 'POST',
    url: 'some/url/for/action/b',
    data: dataFromLastCall,
    success: function(newData){
      dataFromLastCall = newData;
    }
  });
});

请注意,此示例不处理错误情况。

这可能是您想要的:如果当用户单击按钮B时,第一次调用成功时提供的数据不可用会怎么样???@Shubhranshu在这种情况下,在Ajax调用完成之前,用户不能单击按钮B(或任何其他内容),因为在同步Ajax调用完成的整个过程中,浏览器将完全冻结。这是使整个页面暂时无响应的解决方案,这就是为什么不鼓励使用同步Ajax。@Lucas,我认为您应该再次阅读此要求。我不想禁用按钮B,只要第一个按钮持续。即使第一个ajax调用还没有完成,用户也应该单击按钮B。@Shubhranshu以粗体显示内容。但是如果B依赖于某个东西。。。那么这有什么意义呢?您可以“即使第一个ajax调用尚未完成,用户也应该单击按钮B”@Seabizkit,单击按钮B的结果可能依赖于单击按钮A获得的数据,但如果不了解更多关于页面应如何工作的规范,我们不能假设我们应该使整个页面无响应,或阻止用户单击按钮B。。。假设按钮A已经被点击了!这是可行的,但是为什么不通过使用两个变量(
isFirstCallFinished
hasButtonBeenPressed
)来消除不必要的间隔延迟,然后在Ajax回调和click listener中测试
if(isFirstCallFinished&&hasButtonBeenPressed)
,这可能有效,但不是一个合适的解决方案。我认为使用jQuery的方式要好得多。谢谢!听起来很合理。IE有什么问题吗?@robsch-不幸的是,是的。Promissions在除IE之外的所有浏览器上都能正常工作,尽管它们在新的Edge浏览器上完全受支持。有一些第三方JS模块,您可以使用它们来填充相同的行为,例如,我想它可以通过某种方式完成…是的!我从未使用过,但它看起来非常相似,我想我可以看到API是如何工作的。我将编辑我的答案以包含一个替代实现。@robsch我没有意识到jQuery post()方法已经返回了一个延迟对象。这些与承诺非常相似,因此我提供了一个简化的示例,说明如何在您的案例中使用它们。
var queue = [];
var processing = false;
var dataFromLastCall = {}; //data from previous call

function register(callParams){
  queue.push(callParams);
  if(processing == false){
    process(queue);
  }
}

function process(){
  processing = true;
  var call = queue.shift();

  $.when(jQuery.ajax(call)).then(function(){
    if(queue.length > 0){
      process(); //get another
    }else{
      processing = false; //all done
    }
  });
}

$("button.a").click(function(e){
  e.preventDefault();
  register({
    type: 'POST',
    url: 'some/url/for/action/a',
    data: dataFromLastCall,
    success: function(newData){
      dataFromLastCall = newData;
    }
  });
});

$("button.b").click(function(e){
  e.preventDefault();
  register({
    type: 'POST',
    url: 'some/url/for/action/b',
    data: dataFromLastCall,
    success: function(newData){
      dataFromLastCall = newData;
    }
  });
});