我正在尝试更好地理解如何正确地在 Angular 中进行编码,并正在编写自己的自定义模块以更好地理解事物应该如何工作。
我有一个由图像和指令组成的 HTML 标记,即
<img ng-if="!post.tooBig" mydirective resize="0" ng-src="/img/@{{post.Content}}">
我的指令在这里:
.directive('mydirective', [
'$animate','$mediaStack',
function($animate,$mediaStack) {
return {
restrict: 'A',
compile: function(tElement, tAttrs) {
return loader;
}
};
function loader(scope, element, attrs) {
var source = attrs.ngSrc;
var tooLoadBig = parseInt(attrs.resize);
if(tooLoadBig){
var bigImage = new Image();
bigImage.src = source.replace("small.",".");
}
}
}])
想法是这样的:如果图像有 small
附加到其文件名后,我知道它是缩略图。我想在后台加载它的大版本(没有附加小版本的同一文件),以便为灯箱做好准备。
这工作正常,但问题是因为我正在做 compile
中的所有工作。 ,当我设置bigImage.src = source.replace("small.",".");
时它会立即启动,如果我有很多 small
页面上的图像,由于正在进行的所有加载,它会导致页面变慢。
因此我想使用$q
使其一次加载一张图像。
所以移动
var bigImage = new Image();
bigImage.src = source.replace("small.",".");
兑现 promise 。在指令中这样做是最佳做法吗?我的理解是这样做没有意义,我应该使用服务,但我不知道该怎么做。我可以更多地尝试它,但我希望有更多经验的人可以指导我此类事情的最佳实践以及类似工作流程的代码示例,谢谢。
编辑:
指令:
.directive('mydirective', [
'$animate','$mediaStack',
function($animate,$mediaStack) {
return {
restrict: 'A',
compile: function(tElement, tAttrs) {
return loader;
}
};
function loader(scope, element, attrs) {
var source = attrs.ngSrc;
var tooLoadBig = parseInt(attrs.resize);
if(tooLoadBig){
/*var bigImage = new Image();
bigImage.src = source.replace("small.",".");*/
$mediaStack.load(source);
}
}
}])
我的服务:
.factory('$mediaStack', [
'$animate', '$timeout', '$document', '$compile', '$rootScope',
'$q',
'$injector',
function($animate , $timeout , $document , $compile , $rootScope ,
$q,
$injector) {
var OPENED_MEDIA_CLASS = 'media-open';
var theImages = [];
$mediaStack = {};
$mediaStack.load = function(source){
theImages.push(source);
};
$mediaStack.loadRequest = function(theImages) {
deferred.resolve(function(){
var bigImage = new Image();
bigImage.src = theImages[0].replace("small.",".");
});
return deferred.promise;
}
/*var promise = asyncGreet('Robin Hood');
promise.then(function(greeting) {
alert('Success: ' + greeting);
}, function(reason) {
alert('Failed: ' + reason);
}, function(update) {
alert('Got notification: ' + update);
});*/
//above from docs
}
return $mediaStack;
}])
它的工作原理是将图像 url 获取到服务中的数组中,因此我拥有数组中的所有图像,如何使用 $q
正确地基于该数组。我开始了,但大部分$mediaStack.loadRequest
基于 $q
文档,我不确定在这种情况下如何有效地使用它。
编辑2:
我的服务现状:
.factory('$mediaStack', [
'$animate', '$timeout', '$document', '$compile', '$rootScope',
'$q',
'$injector',
function($animate , $timeout , $document , $compile , $rootScope ,
$q,
$injector) {
var OPENED_MEDIA_CLASS = 'media-open';
var theImages = [];
var theLoadedImages = [];
var thePromises = [];
var loading = false;
$mediaStack = {};
$mediaStack.load = function(source){
theImages.push(source);
var mainDeferred = $q.defer();
if(loading)
{
thePromises.push(mainDeferred.promise);
console.log(thePromises);
$mediaStack.myRaceFn(thePromises).then(function() {
console.log("Fire!");
loading=true;
$mediaStack.loadRequest(theImages[0]).then(function(bigImage){
console.log(bigImage);
theImages.shift();
theLoadedImages.push(bigImage);
loading = false;
mainDeferred.resolve();
});
});
}
if(!loading)
{
loading = true;
$mediaStack.loadRequest(theImages[0]).then(function(bigImage){
console.log(bigImage);
theImages.shift();
theLoadedImages.push(bigImage);
loading = false;
mainDeferred.resolve();
});
}
}
$mediaStack.loadRequest = function(source){
var deferred = $q.defer();
var bigImage = new Image();
bigImage.src = source.replace("small.",".");
bigImage.onload = function() {
deferred.resolve(bigImage);
}
return deferred.promise;
}
//.race implementation in old angular
$mediaStack.myRaceFn = function (promises){
return $q(function(resolve, reject) {
promises.forEach(function(promise) {
console.log(promise);
promise.then(resolve, reject);
});
});
}
//.race implementation in old angular
return $mediaStack;
}])
我已经非常接近了,我看到了一系列的 Promise,但我从来没有达到能够再次触发它们的程度,所以我有 10 个图像,并且我得到了 9 个 Promise 的数组。我该如何解雇他们?
我预计它会发生在这里:
$mediaStack.myRaceFn(thePromises).then(function() {
console.log("Fire!");
loading=true;
$mediaStack.loadRequest(theImages[0]).then(function(bigImage){
但我从来没有明白console.log()
.
相反,我在 $mediaStack.myRaceFn()
中收到控制台消息向我展示每个 promise (最后是 9),但它们只是坐在那里?我错过了一些东西。
我想我可能太早解决 mainDeferred...
最佳答案
我认为,你的预感是好的。您需要一项服务来处理对图像的请求。请记住,浏览器仅限于向单个服务器/代理发送多个并发请求 ( Max parallel http connections in a browser? )
理论上,您可以创建一个 promise 并通过两种方式绑定(bind)公开它,甚至使用属性驱动加载。像这样的东西:
HTML:
<img mydirective="booleanValue" />
在我的指令
中:
$scope.$watch('mydirective', function (newVal, oldVal) {
if (newVal === true) {
//perform the load
}
});
但我认为,提供服务会更好。您可以从这一点决定加载什么以及何时加载。它不仅限于 UI 组件,您不仅可以从指令上下文加载更大的图像。 因此,在服务中,基本代码是:
module.factory('$mediaStack', ['$q', function($q) { //first of all you don't need all of these dependencies, keep the list short
var imgDict = {}; //Image dictionary where we're going to keep promises and loaded images
var loadingNow = 0;
function loadImage(img) {
//Send request and return promise
var deferred = $q.defer();
var bigImage = new Image();
bigImage.onload = function() {
deferred.resolve(bigImage);
}
bigImage.src = source.replace("small.", ".");
return deferred.promise;
}
var $mediaStack = function(img) {
var deferred = $q.defer(); //the main deferred task
if (loadingNow > 4) { //if we're loading 5 images or more, defer the task untill one of the Images is loaded
var promises = []; //an array of promises, list of images which are loading now
Object.keys(imgDict).forEach(function(key) {
if (!(imgDict[key] instanceof Element)) { //Check if the item from dictionary is a promise or loaded image
promises.push(imgDict[key]); //if it's a promise, add it to the list
}
});
$q.race(promises).then(function() { //use the list, to create a race: when one of the promises resolves (image is loaded) we can fire our function again (we probably have less than 5 Images loading at the same time)
$mediaStack(img).then(function(data) { //call the function again
deferred.resolve(data); //as we return a promise form that function we have to resolve main deferred object
});
});
}
if (!(img in imgDict)) { //when the image is not loaded yet
loadingNow++; //increase the number of images being loaded
imgDict[img] = loadImage(img).then(function(imgDOM) { //and load the image
imgDict[img] = imgDOM;
deferred.resolve(imgDOM); //once it's loaded resolve the main deferred task
loadingNow--;
});
} else {
deferred.resolve(imgDict[img]);
}
return deferred.promise; //promise to the main deferred task
}
return $mediaStack;
}]);
promise 和延迟任务背后的总体思路: Deferred 对象是一个延迟任务,它有一个 Promise 参数 - 回调的入口点,一旦延迟任务完成就会调用它。
查看 $q 文档了解更多信息:angular $q
以及如何编写服务:https://docs.angularjs.org/guide/services
什么是 promise ? The general idea and native API
希望有帮助。
关于javascript - 从指令到服务的 Angular 队列操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43150405/