在 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) 在你的 resizeImage
和 fetchImage
中你忘记了在本地声明 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>
我的目标是获取图像 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) 在你的 resizeImage
和 fetchImage
中你忘记了在本地声明 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>