【问题标题】:Asynchronous map/reduce in Javascript/JQueryJavascript/JQuery 中的异步映射/减少
【发布时间】:2012-07-21 01:12:09
【问题描述】:

在 Javascript (jQuery) 中执行以下操作的惯用方式是什么?

  • 生成一组异步作业
  • 收集部分结果
  • 当每个作业都完成后,合并部分结果

上述可以通过以下方式实现(为简单起见,假设请求按顺序处理;在更现实的情况下,计数器会完成这项工作):

var results = new Array();
$.each(objs, function(i,obj)){
  $.getJSON(make_async_request(obj), function(data) {
    results[data.key] = data.value;
    if (i == objs.length-1)
      elaborate(results);
  }
});

在我看来很难看。有什么建议吗?

【问题讨论】:

  • 用多个 HTTP 请求猛击服务器并不一定会让事情变得更好。当然我们不知道你的情况,但你的代码看起来不错(除了检查i==objs.length-1的位,因为没有保证最后一个请求将最后完成)。
  • 你说得对。我已经用我在这里所做的假设更新了这个问题。

标签: javascript jquery idioms


【解决方案1】:

你可以像这样使用jQuery.Deferred

   $.when($.get('/a/'), $.get('/b/')).then(function() {
       // all gets are ready
   });

如果您需要将所有结果合并到一个对象中,您可以在then 回调中循环参数:

$.when($.get('/a/'), $.get('/b/')).then(function() {
    var results = {},
        args = Array.prototype.slice.call(arguments),
        data;
    $.each( args, function(i, resp) {
        data = resp[0];  // resp is the results array
        results[data.key] = data.value;
    });
    console.log(results);
});

【讨论】:

    【解决方案2】:
    var results = [], ajaxes = [];
    $.each(objs, function(i,obj)){
        var xhr = $.ajax({
            url: make_async_request(obj),
            dataType: 'json'
        }).done(function() {
            results[data.key] = data.value;
            if (i == objs.length-1) elaborate(results);
        });
        ajaxes.push(xhr);
    });
    
    $.when.apply(null, ajaxes).done(function() {
        alert('all ajaxes are done');
    });
    

    【讨论】:

      【解决方案3】:

      您检查作业的完成情况是不充分的,因为$.getJSON 是异步的,并在发出请求后立即返回,因此一旦发出最后一个请求,您的检查就会评估为真。

      利用$.ajax$.getJSON 返回Deferreds 的事实,您可以有效地做您想做的事。使用$.when$.map 收集所有处理请求的数据。看看 jQuery 中的 deferred/promise api,它们非常强大,并且允许像下面这样的单线工作:

      $.when($(objs).each(function (i, obj) { return $.getJSON(make_async_request(obj)); }), elaborate);
      

      【讨论】:

      • 您能详细说明一下吗?我本来希望在“精心制作”之前有一个“地图”来代替“每个”和“然后”。 each-function 中的返回值看起来很奇怪,与“map”相比,每个函数只使用返回值来决定是否要中止循环。因此,您可以首先删除“return”关键字。
      【解决方案4】:

      完全披露:公然堵塞我自己的库,queue-flow :)

      q(objs)
          .map(q.async(function(obj, callback) {
              $.getJSON(make_async_request(obj), callback);
          }))
          .reduce(function(results, data) {
              results[data.key] = data.val;
              return results;
          }, elaborate, {});
      

      queue-flow 使用队列的概念使异步功能代码看起来几乎完全同步。 :) 它还可以让你用源代码组织做一些非常有趣的事情,看看 GitHub 上自述文件中的.branch 方法。 (刚刚更新了我的示例以匹配问题示例中的更改。)

      也可以写成:

      q(objs)
          .map(make_async_request)
          .map(q.async($.getJSON))
          .reduce(function(results, data) {
              results[data.key] = data.val;
              return results;
          }, elaborate, {});
      

      这真的很酷,但是对于不熟悉的人来说可能需要一点时间来摸索。 :)

      【讨论】:

        【解决方案5】:

        截至 2018 年的更新: 现在你可以使用

        results = await Promise.all(objs.map(someAsyncFunction));
        

        其中objs 应该是某个列表,someAsyncFunction 获取该列表的一个元素并返回一个带有结果的承诺。 有关更多详细信息,请参阅此问题的答案:Best way to call an async function within map?

        【讨论】:

          猜你喜欢
          • 2019-07-10
          • 1970-01-01
          • 1970-01-01
          • 2012-06-04
          • 2020-01-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-05
          相关资源
          最近更新 更多