如何处理本身异步触发的异步ajax调用

时间:2017-01-25 12:33:21

标签: javascript jquery ajax asynchronous

我有这个问题:

用户点击按钮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 () {

        })
});

注意:最终目标是加快改善用户体验的过程。

(随意改进问题的标题。我不能更好地表达。)

4 个答案:

答案 0 :(得分:3)

如果您可以使用最新版本的ECMAScript规范,那么您可能需要查看使用Promises。有一个非常好的explanation of Promises on Google's documentation for the web,但它们基本上是我们可以用来等待异步完成事情的对象。

如果你在Promise对象中包装你的第一个ajax调用,那么你可以稍后再回来(在你的情况下,在用户点击按钮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
      });
  });
});

答案 1 :(得分:1)

我的想法是基于按钮点击排队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;
    }
  });
});

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

答案 2 :(得分:0)

var isFirstCallFinished = false;


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

$('button.b').click(function () {
// now I need something from returnedData, which may not be available yet
setInterval(function () {
 if(isFirstCallFinished)  {
 // Cancel Interval
 // Call postMe()
 }
}, 1000);
});
function postMe() {
 $.post('some/url/for/action/b', {something: returnedData.something})
    .done(function () {

    })
  });
 }

答案 3 :(得分:-2)

您实际上想要进行同步调用。我建议您使用$.ajax代替$.post,因为它可以更加自定义。

您的按钮B功能将是这样的:

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

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