【问题标题】:How to wait for an async call from a callback using jQuery?如何使用 jQuery 等待来自回调的异步调用?
【发布时间】:2012-08-28 03:57:36
【问题描述】:

我正在使用基于 select2 jQuery 的组合框替换,我必须定义一个回调来处理我从 json rest web 服务接收到的数据。

问题是,在同一个回调中,我必须发出另一个 GET 请求来获取匹配记录的总数,以便 select2 可以决定是否需要加载更多结果(它具有无限滚动功能)

代码是这样的:

$("#country").select2({
  ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
    url: 'http://localhost:9000/api/countries',
    dataType: 'json',
    data: function(term, page) {
      return {
        filter: term,
        page: page,
        len: 10
      };
    },
    results: function(data, page) {
      return {
        results: data, more: ????
      };
    }
  }
});

问题是我不知道如何发出异步请求(我正在发出跨域请求,并且文档说在这种情况下不支持异步)并等待它完成,然后再从结果回调。

select2 页面的例子是这样的:

results: function (data, page) {
  var more = (page * 10) < data.total; // whether or not there are more results available
  // notice we return the value of more so Select2 knows if more results can be loaded
  return {results: data.movies, more: more};
}

问题是我的 Web 服务返回来自不同端点的记录总数,所以我必须发出另一个请求,如下所示:http://localhost:9000/api/countries?filter=term

有什么想法吗?

【问题讨论】:

    标签: javascript jquery ajax asynchronous


    【解决方案1】:

    您不能等待 javascript 中的异步回调。您必须根据来自实际回调的异步响应来重组代码以完成所有未来的工作。

    如果您需要进行多个连续的 ajax 调用,则发出第一​​个,然后在第一个 ajax 调用的成功处理程序或响应处理程序中,发出第二个 ajax 调用,并在第二个的响应处理程序中,您对数据执行任何你想做的事情。

    如果发现您使用的是.select2() 框架。在框架中,结果回调是 ajax 调用返回的地方。在该函数中,您将使用普通的 jQuery ajax 调用发出第二个 ajax 调用,并在第二个 ajax 调用的成功处理程序中,您将对返回的最终数据执行任何您想要执行的操作。您将无法使用结果回调的正常返回值,因为您在需要返回的点上还没有最终数据。我认为这只是.select2() 的一个限制,因为它只支持单个ajax 调用。这只是意味着您不能使用一点内置行为,并且必须自己使用自己的代码应用结果,但这并不意味着您必须丢弃 .select2() 来处理您使用的所有其他内容为。

    看起来您可能只想直接挂钩 change 事件而不使用它们的内置 ajax 东西,因为如果您需要两个序列化的 ajax 调用,它看起来并不能真正为您提供很多东西。

    【讨论】:

    • 我明白你的意思,并且担心这将是我问题的唯一答案(+1 表示真诚 :-)。问题是我无法控制 select2 的作用(不是不分叉,我的意思是......)你能想到在给定情况下实现它的任何其他方法吗?
    • @opensas - 如果您要解决的问题是如何在使用 .select2 时进行两次连续的 ajax 调用,我已将其添加到答案的末尾。
    • 很好的答案 jfriend00,我将看看 select2 代码,看看我是否可以设法摆脱两个序列化的 ajax 调用,同时我使它与同步请求一起工作。 . 看我自己的答案...
    【解决方案2】:

    我在select2上研究了源代码,最后得出了这个解决方案

    var ajax = {
      url: 'http://localhost:9000/api/countries',
      len: 3,
    };
    
    $("#country").select2({
      query: function(options) {
        var data = {
          filter: options.term,
          page: options.page,
          len: ajax.len
        };
        $.ajax({
          url: ajax.url,
          data: data,
          dataType: 'json',
          type: 'GET',
          success: function(data) {
            $.ajax({
              url: ajax.url + '/count',
              data: { filter: options.term },
              dataype: 'json',
              success: function(resp) {
                var total = parseInt(resp, 10);
                var more = (options.page * ajax.len) < total;
                options.callback({results: data, more: more});
              }
            });
          }
        });
      },
    });
    

    如您所见,当第一次 fetch (ajax.url) 完成时,我发出另一个请求 (ajax.url + '/count'),只有当第二个请求完成时,我才调用 options.callback,有效地序列化两个 ajax 调用...

    实际上ajax function from select2 有更多功能,例如限制和丢弃无序响应,我也只是移植了它们,但为了不使示例复杂化,我将它们排除在此响应之外...

    【讨论】:

      【解决方案3】:

      除了 jfriend00 的回答(这很好,顺便说一句),我发现了 followgin 解决方法,它基本上是同步发出请求,尽管 jquery docs 它似乎工作(至少对于 chromium 18.0 和 jquery 1.8.0 )

      我只是发布它以防有人发现它有用...

      var config = {
        url: 'http://localhost:9000/api/countries',
        len: 20,
        term: ''
      }
      $("#country").select2({
        ajax: { 
          url: config.url,
          dataType: 'json',
          data: function(term, page) {
            config.term = term;
            return {
              filter: term,
              page: page,
              len: config.len
            };
          },
          results: function(data, page) { // parse the results into the format expected by Select2.
            var more = false;
            $.ajax({
              url: config.url + '/count',
              data: { filter: config.term },
              cache: false,
              async: false,
              success: function(resp) {
                var total = parseInt(resp, 10);
                more = (page * config.len) < total;
              },
              async:false
            });
            return {
              results: data, more: more
            };
          }
        }
      });
      

      【讨论】:

      • aysnc:false 对用户真的很不友好。它锁定浏览器(它不会响应任何 UI 交互),直到 ajax 调用完成。这是非常不鼓励的,并且几乎从来都不是您的用户交互工作的最佳方式。我知道这是一种解决方法,但你真的应该制定一个更好的长期设计。
      • 请看我的新答案 ;-)
      猜你喜欢
      • 2017-06-29
      • 2018-04-06
      • 2016-02-14
      • 2012-07-19
      • 1970-01-01
      • 2021-06-30
      • 1970-01-01
      • 1970-01-01
      • 2019-02-15
      相关资源
      最近更新 更多