javascript - 从指令到服务的 Angular 队列操作

标签 javascript angularjs

我正在尝试更好地理解如何正确地在 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/

相关文章:

angularjs - 如何使用 ng-option 设置 select 元素的默认值

javascript - Angular 1.5 组件元素上的 ng-class

使用 Ajax 将 Javascript 变量传递给 PHP

javascript - 对 TypeScript 类型的 ChildProcess 类的引用

JavaScript 如何使用公共(public) API 和纯 JavaScript 更改输入值

javascript - 在 Angularjs 中刷新后保留所选行

javascript - 使用 Angular JS 调用具有多个数据的 Web 服务

javascript - 如何在 TypeScript 中使用 document.getElementById() 方法?

javascript - 如何将 Blob 转换为图像(JPG 格式)

javascript - 地理围栏并发送推送通知