在 angularjs 中使用 $q 在数组上循环异步函数

Looping async function over array with $q in angularjs

我的目标是获取图像 URL 数组(从 API 获得,它们的 CORS headers 已设置)并对其进行循环,使用 xhr 获取每个图像,调整其大小canvas 元素,然后生成另一个数组,其条目是调整大小的图像的数据。我的问题是只有数组中的最后一个条目被正确处理,其他所有内容都是空白图像 - 我的猜测是 $q.deferred() 在循环的每次迭代中被覆盖,但我没有真的不知道,因为我是 promises 和 async 的新手。另外,我在 AngularJS v1.25 中写这篇文章。您可以在 http://codepen.io/anon/pen/razwyZ?editors=101 查看我的代码。我也会post这里:

var myApp = angular.module('myApp', []);

//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});

myApp.controller('MyCtrl', ['$scope', '$q',
  function($scope, $q) {

    resizeImage = function(imgSrc) {
      var deferred = $q.defer();
      // console.log(imgSrc);
      // console.log("resizing image");
      image = new Image();
      image.src = imgSrc;
      image.onload = function() {
        var canvas = document.createElement("canvas");
        canvas.height = 300;
        canvas.width = 300;

        var ctx = canvas.getContext("2d");
        ctx.drawImage(image, 0, 0, 300, 300);
        deferred.resolve(canvas.toDataURL("image/png"));
      };

      return deferred.promise;
    };

    fetchImage = function(src) {
      var deferred = $q.defer();
      var xhr = new XMLHttpRequest();
      xhr.responseType = 'blob';

      xhr.onload = function() {
        var url = URL.createObjectURL(xhr.response);
        image = new Image();
        resizeImage(url)
          .then(function(dataUri) {
            deferred.resolve(dataUri);
          });
      };

      xhr.open('get', src);
      xhr.send();

      console.log(deferred.promise);
      return deferred.promise;
    };

    $scope.picUrls = ["https://res.cloudinary.com/mediocre/image/upload/v1415293060/od9rkw2xtdtfsfiblq6k.png", "https://res.cloudinary.com/mediocre/image/upload/v1415293108/pgnbdgx0aamspgcxzwmx.png", "https://res.cloudinary.com/mediocre/image/upload/v1415294024/z7ohumjr5vs87e1nn5fl.png", "https://res.cloudinary.com/mediocre/image/upload/v1422407613/mzuce3ooqpcewddq9iih.png"];
    $scope.picArray = [];

    for (i = 0; i < $scope.picUrls.length; i++) {
      console.log("executing loop")
      $scope.picArray.push(fetchImage($scope.picUrls[i]));
    }

    $q.all($scope.picArray).then(function(imgArray) {
      console.log("executing q.all statement");
      $scope.picArray = imgArray;
      console.log($scope.picArray);
    });

  }
]);
<!DOCTYPE html>
<html>

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>

<body>
  <div ng-app="myApp" ng-controller="MyCtrl">
    <div ng-repeat="pic in picArray">
      <img src="pic">
    </div>
  </div>
</body>

</html>

你实际上正确地做了 deferred 的承诺。我不知道你为什么使用 $q.defer()xhr 而你本可以使用 $http.get 而 returns 已经是一个承诺。

不过你有两个小问题:

1) 你应该使用 ng-src 而不是 src,并且表达式应该在花括号中。如果没有 ng-src,浏览器将尝试从位置 "/{{pic}}".

下载

2) 由于数组中有重复的条目,ng-repeat 出现错误。要修复,您需要添加 track by(通常,要查看更好的错误,请从 angular.min.js.

中删除 .min
<div ng-repeat="pic in picArray">
  <img ng-src="{{pic}}">
</div>

编辑:

您还有一些问题。

1) 在你的 resizeImagefetchImage 中你忘记了在本地声明 image 变量,因为你没有使用 "use strict" (一个坏主意,对吧那里)你已经有效地创建了一个全局 image 变量。

改为:

var image = new Image();

2) 不要重复使用存储承诺的数组来存储图像。这会生成失败的 HTTP 请求以检索图像的 src

使用不同的变量:

$scope.pics = [];

// then in `$q.all`
$scope.pics = imgArray;

ng-repeat 以上:

<div ng-repeat="pic in pics track by $index">
  <img ng-src="{{pic}}">
</div>

fork of your codepen