角连锁许多请求

时间:2014-08-30 22:28:39

标签: javascript angularjs coffeescript angular-promise angular-http

我尝试构建一个可以获取CSV数据,将其转换为行,然后为每行发出API请求,将格式化结果添加到输出字符串的服务。

换句话说,我的(Coffeescript)代码如下所示:

  $s.batchFetch = ->
    return unless $s.csv
    $s.output = ''
    for row in $s.csv.split("\n")
      $s._addToOutput(row)

$s._addToOutput()函数正确地使用该行进行API调用,对其进行格式化,并将格式化的响应添加到我的输出字符串($s.output)。基本上,这样的事情:

$s._addToOutput (row) = -> 
  formattedResponse = ''
  $http.get("api/request/path/row-specific-whatever")
    .success (res) ->
      formattedResponse = $s._format(res)
    .then ->
      $s.output += formattedResponse

问题是输出字符串中格式化响应的顺序似乎是随机/变量。对于某些行,API看起来比其他行更快/更长,并且首先添加的响应首先被添加 - 不考虑我的rows变量的顺序。

我认为解决方案是某种角度承诺链接,la:

$s._addToOutput(row).then ->
  $s._addToOutput(secondRow).then ->
    $s._addToOutput(thirdRow).then ->
      ...

但我有一个不可预测的行数,我希望能够基本上只是说:"为每行做一次API调用,一个接一个"

有人能想到一个好方法吗?我现在可能不会直接思考,但我很难过。

谢谢!

萨莎

编辑 - 给了ryeballar的解决方案,但我的实现并没有真正阻止重新排序的问题。很确定这是我的错误,所以如果有人发现任何事情,请告诉我:

(注意,我必须调整解决方案,因为我连续两次请求每一行 - 第一个用于"场地"第二个用于"照片" of我找到的地点。另外,yamlify =='格式'。)

$s.batchFetch = function() {
  if (!$s.csv) {
    return;
  }
  $s.output = '';
  return $scope.csv.split("\n").reduce(function(promise, row) {
    var rowPromise, split;
    split = row.split(',');
    rowPromise = $s._getVenue(split[0], split[1]).success(function(res) {
      var venue;
      venue = res.response.groups[0].items[0].venue;
      $s._getPhoto(venue).success(function(resp) {
        var photo;
        photo = $s._photoUrl(resp);
        return $s.output += $s._yamlify(venue, row, photo);
      });
    });
    return promise.then(rowPromise);
  }, $q.when());
};

注意 - getVenue()getPhoto()只是对$http的调用,因此他们会返回回复successerrorthen的对象}等等photoUrl()只是一个辅助函数,用于将响应对象解析为新的API路径。

最近的努力,仍在随机重新排序 - 是_getVenue_getPhoto只是$http.get(path)来电:

$s.batchFetch = function() {
  if (!$s.csv) {
    return;
  }
  $s.output = '';
  return $s.csv.split("\n").reduce(function(promise, row) {
    var rowPromise, split;
    split = row.split(',');
    rowPromise = $s._getVenue(split[0], split[1]).success(function(res) {
      var venue;
      venue = res.response.groups[0].items[0].venue;
      return $s._getPhoto(venue).success(function(resp) {
        var photo;
        photo = $s._photoUrl(resp);
        return $s.output += $s._yamlify(venue, row, photo);
      });
    });
    return promise.then(rowPromise);
  }, $q.when());
};

1 个答案:

答案 0 :(得分:2)

您可以通过从一个到另一个调用每个承诺来使用.reduce()。初始值是一个承诺,它将立即解析$q.when(),然后调用rowPromise,这也是一个承诺,每当调用then()时都会产生链接效果。

.controller('CsvController', function($scope, $q) {

  $scope.csv = '.....';
  $scope._format = function() {/*...*/};

  $scope.batchFetch = function() {
    $scope.output = '';
    return $scope.csv.split('\n').reduce(function(promise, row) {
       return promise.then(function() {
          return $http.get("api/request/path/row-specific-whatever", {row: row})
                          .success(function(res) {
                             $scope.output += $scope._format(res);
                          });
       });
    }, $q.when());
  };

});

<强>更新 我已经更新了上面的代码,它应该是一个回调,而不是在迭代过程中调用$http请求。所以你的代码应该是这样的:

$s.batchFetch = function() {
  if (!$s.csv) {
    return;
  }
  $s.output = '';
  return $s.csv.split("\n").reduce(function(promise, row) {
    return promise.then(function() {
       var split = row.split(',');
       return $s._getVenue(split[0], split[1]).success(function(res) {
         var venue = res.response.groups[0].items[0].venue;
         return $s._getPhoto(venue).success(function(resp) {
           var photo = $s._photoUrl(resp);
           return $s.output += $s._yamlify(venue, row, photo);
         });
       });
    });
  }, $q.when());
};

当我阅读你的代码时,它似乎正在落入回调地狱。或者,最好像这样构造它:

$s.batchFetch = function() {
  if (!$s.csv) {
    return;
  }
  $s.output = '';
  return $s.csv.split("\n").reduce(function(promise, row) {
    var split, venue, photo;
    return promise
       .then(function() {
          split = row.split(',');
          return $s._getVenue(split[0], split[1]);
       }).then(function(response) {
          venue = response.data.response.groups[0].items[0].venue;
          return $s._getPhoto(venue);
       }).then(function(response) {
          photo = $s._photoUrl(response.data);
          return $s.output += $s._yamlify(venue, row, photo);
       });
  }, $q.when());
};