2015-08-25 3 views
2

я предзагрузка изображения со следующим кодом:Resolve обещание, когда все изображения будут загружены

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(); 

Я думал, что изображения были загружаются правильно, потому что я мог видеть «Решен» журнал.

Позже кто-то указал, что приведенный выше код не гарантирует, что все изображения загружены до разрешения обещания. Фактически, разрешено только первое обещание.

Мне было рекомендовано использовать $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(); 

Это работает, но я хочу, чтобы понять:

  1. что происходит в каждой функции;
  2. , почему мне нужно $q.all, чтобы убедиться, что все изображения загружены до разрешения обещаний.

relevant docs несколько загадочны.

+0

Откуда у вас это 'imageArray.forEach.onload'? –

+0

@HenriqueBarcelos, вы имеете в виду, что это должно быть 'imageArray.onload' вместо этого? –

+0

Интересно, почему вы говорите о «первом обещании» ... в вашем первом фрагменте есть только одно обещание. – Bergi

ответ

4

Заканчивать этот plunkr.

Ваша функция:

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; 
    }); 
} 

Идея очень похожа на ответ Henrique, но обработчик OnLoad используется для разрешения каждого обещания, и OnError используется для отклонения каждого обещания.

Чтобы ответить на ваши вопросы:

1) Promise завод

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

строит 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 передается массив обещаний, then принимает функцию, которая передается массив с решениями всех первоначальных обещаний.

+1

Я не вижу разницы. Что вы подразумеваете под «*, но обработчик onload используется для разрешения каждого обещания, а onerror используется для отклонения каждого обещания. *"? Кстати, вы должны опубликовать свой код, особенно функцию loadImage, в своем ответе, а не на каком-то плунжере. – Bergi

+1

Ницца, вы правы, и он отредактирован. Чтобы ответить на ваш вопрос, вместо определения разрешения/отклонения в обработчике onload, я доверяю обработчику onerror, чтобы решить, следует ли отклонять. Обработчик onload может только разрешить. В зависимости от варианта использования, но другое решение может быть лучше, если вам нужны более гранулированные критерии отклонения. Кроме того, я хотел на самом деле объяснить обещания немного более подробно на OP – ShaharZ

+0

А, правильно. Обратите внимание, что Henrique также должен был это исправить, см. Комментарии к его ответу для деталей :-) – Bergi

2

Я не привык к угловому библиотеке обещание, но идея заключается в следующем:

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); 
}); 
+0

Вы не указали, что нужно добавить обработчик 'onerror'? – Bergi

+0

Вы имели в виду обработчик 'onerror' для элемента' Image'? –

+0

Да, точно. OP использовал один, поэтому я задавался вопросом, достаточно ли вашего теста для размеров в обработчике onload? – Bergi

Смежные вопросы