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

时间:2015-01-28 23:28:41

标签: javascript angularjs asynchronous angular-promise

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

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>

1 个答案:

答案 0 :(得分:0)

你实际上正确地做了延期的承诺。我不知道为什么你使用$q.defer()xhr时,你刚刚使用了$http.get,它已经返回了一个承诺。

你有两个小问题:

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

2)由于数组包含重复条目,因此ng-repeat出错。要解决此问题,您需要添加track by(通常,为了查看更好的错误,请从.min移除angular.min.js

<div ng-repeat="pic in picArray">
  <img ng-src="{{pic}}">
</div>

修改

你还有一些问题。

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

将其更改为:

var image = new Image();

2)不要重复使用存储promises的数组来存储图像。这会生成失败的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