javascript 加载所有图像时解决承诺

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32214924/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 14:59:13  来源:igfitidea点击:

Resolve promise when all images are loaded

javascriptangularjsangular-promise

提问by U r s u s

I was preloading images with the following code:

我使用以下代码预加载图像:

    function preLoad() {
        var deferred = $q.defer();
        var imageArray = [];
        for (var i = 0; i < $scope.abbreviations.length; i++) {
            imageArray[i] = new Image();
            imageArray[i].src = $scope.abbreviations[i].imgPath;
        }
        imageArray.forEach.onload = function () {
            deferred.resolve();
            console.log('Resolved');
        }
        imageArray.forEach.onerror = function () {
            deferred.reject();
            console.log('Rejected')
        }
        return deferred.promise;
    }
    preLoad();

I thought images were all loading correctly because I could see the 'Resolved' log.

我认为图像都正确加载,因为我可以看到“已解决”日志。

Later somebody pointed out that the code above doesn't guarantee that all images are loaded before resolving the promise. In fact, only the first promise is resolved.

后来有人指出,上面的代码并不能保证在解决承诺之前加载所有图像。事实上,只有第一个承诺被解决了。

I was advised to use $q.allapplied to an array of promises instead. This is the resulting code:

我被建议改用$q.all应用于一系列承诺。这是结果代码:

    function preLoad() {
        var imageArray = [];
        var promises;
        for (var i = 0; i < $scope.abbreviations.length; i++) {
            imageArray[i] = new Image();
            imageArray[i].src = $scope.abbreviations[i].imgPath;
        };

        function resolvePromises(n) {
            return $q.when(n);
        }
        promises = imageArray.map(resolvePromises);
        $q.all(promises).then(function (results) {
            console.log('array promises resolved with', results);
        });
    }
    preLoad();

This works, but I want to understand:

这有效,但我想了解:

  1. what's happening in each function;
  2. why I need $q.allto make sure all images are loaded before resolving the promises.
  1. 每个函数中发生了什么;
  2. 为什么我需要$q.all确保在解决承诺之前加载所有图像。

The relevant docsare somewhat cryptic.

相关的文档有些神秘。

回答by ShaharZ

Check out this plunkr.

看看这个plunkr

Your function:

你的功能:

function preLoad() {

    var promises = [];

    function loadImage(src) {
        return $q(function(resolve,reject) {
            var image = new Image();
            image.src = src;
            image.onload = function() {
              console.log("loaded image: "+src);
              resolve(image);
            };
            image.onerror = function(e) {
              reject(e);
            };
        })
    }

    $scope.images.forEach(function(src) {
      promises.push(loadImage(src));
    })

    return $q.all(promises).then(function(results) {
        console.log('promises array all resolved');
        $scope.results = results;
        return results;
    });
}

The idea is very similar to Henrique's answer, but the onload handler is used to resolve each promise, and onerror is used to reject each promise.

这个想法与 Henrique 的答案非常相似,但 onload 处理程序用于解析每个承诺,而 onerror 用于拒绝每个承诺。

To answer your questions:

回答您的问题:

1) Promise factory

1) 承诺工厂

$q(function(resolve,reject) { ... })  

constructs a Promise. Whatever is passed to the resolvefunction will be used in the thenfunction. For example:

构造一个 Promise。传递给resolve函数的任何内容都将在函数中使用then。例如:

$q(function(resolve,reject) {
     if (Math.floor(Math.random() * 10) > 4) {
         resolve("success")
     }
     else {
         reject("failure")
     }
}.then(function wasResolved(result) {
    console.log(result) // "success"
}, function wasRejected(error) {
    console.log(error) // "failure"
})

2) $q.all is passed an array of promises, thentakes a function which is passed an array with the resolutions of all the original promises.

2) $q.all 被传递一个承诺数组,then接受一个函数,该函数被传递一个具有所有原始承诺分辨率的数组。

回答by Henrique Barcelos

I'm not used to angular promise library, but the idea is as follows:

我不习惯 angular promise 库,但想法如下:

function getImagePromise(imgData) {
    var imgEl = new Image();
    imgEl.src = imgData.imgPath;

    return $q(function(resolve, reject){
        imgEl.addEventListener('load', function(){
            if ((
                   'naturalHeight' in this 
                    && this.naturalHeight + this.naturalWidth === 0
                ) 
                || (this.width + this.height == 0)) {
                reject(new Error('Image not loaded:' + this.src));
            } else {
                resolve(this);
            }
        });

        imgEl.addEventListener('error', function(){
            reject(new Error('Image not loaded:' + this.src));
        });
    })
}

function preLoad() {
    return $q.all($scope.abbreviations.map(getImagePromise));
}

// using
preLoad().then(function(data){
    console.log("Loaded successfully");
    data.map(console.log, console);
}, function(reason){
    console.error("Error loading: " + reason);
});