Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   действия после окончания img.onload (https://javascript.ru/forum/events/68183-dejjstviya-posle-okonchaniya-img-onload.html)

Prowler 31.03.2017 08:58

действия после окончания img.onload
 
Доброго времени суток!
Задача получить из массива с файлами изображений объекты base64.
Есть простенький кусок кода стянутый с стековера:
function getBase64(file) {
   var reader = new FileReader();
   reader.readAsDataURL(file);
   reader.onload = function () {
     console.log(reader.result);
     return reader.result; //добавляем возврат из функции
   };
   reader.onerror = function (error) {
     console.log('Error: ', error);
   };
}

и пробуем прогнать массив через эту функцию, например так:
//массив с картинками доступен в переменной test
function getArray(){
var newArray=[];
for(var i=0; i<test.length; i++){
newArray.push(getBase64(test[i]));
}
return newArray;
}

Конечно возвращается пустой массив, т.к. функции не дожидаются завершения события onload и выполняются возвращая в массив undefined. Можно ли сохраняя текущий подход как-то дождаться завершения чтения файла и только тогда продолжить выполнения функций? Ну или другим методом добиться искомого результата?

рони 31.03.2017 17:00

Prowler,

function getBase64(test, callback) {
    var newArray = [];
    for (var i = 0; i < test.length; i++) {
        var reader = new FileReader;
        reader.onload = function() {
            newArray.push(reader.result);
            if (test.length == newArray.length) callback(newArray)
        };
        reader.onerror = function(error) {
            console.log("Error: ", error)
        };
        reader.readAsDataURL(test[i])
    }
};

Alexandroppolus 31.03.2017 17:09

примерно так. Написано на коленке, не проверял, дебажить тебе )

function getArray(test) {
	return Promise.all(test.map(function(file) {
		return new Promise(function(resolve) {
 			var reader = new FileReader();
 			reader.onload = function () {
	 			resolve(reader.result);
 			};
 			reader.onerror = function (error) {
	 			resolve(null);
 			};
			reader.readAsDataURL(file);
		});
	}));
}

// использование
getArray(test).then(function(arr) {
	// в массиве arr будут все base64
});

Prowler 06.04.2017 21:07

рони,
Благодарю.
Alexandroppolus,
Попробовал разобраться таки с незнакомой темой промисов. Кажется вот разобрался, но возник вопрос. Не отходя от вашего примера: как узнать, что все изображения уже загрузились?
Попробую объяснить. Функцию которая конвертируют изображения вызывает внешний код. В ответ он хочет получить уже готовый массив в base64. Если здесь
getArray(test).then(function(arr) {
});

arr присвоить другой переменной, например, из замыкания или глобальной, мы получим наш массив как хотелось. Но пока они туда будут грузиться, внешний код может уже их использовать.
Вижу только выход из ситуации с переходом на цепочку промисов. Других вариантов нет?

Решил дописать, думаю, понятней будет. Это делается как часть большой формы. Кроме загрузки изображений есть другие данные. Чтобы упростить задачу, все они должны собраться в JSON. Пользователь может попытаться отправить данные пока идет загрузка картинок(маловероятно, но все же). Собрать остальные данные - легко. Но как проверить что массив уже содержит все base64 ну и... вернуть его?

Alexandroppolus 07.04.2017 09:54

Prowler,
вызов функция, переданной в then - это и есть тот момент, когда все картинки загрузились. Это обеспечивается методом Promise.all - он получает массив промисов, ждет когда они все зарезолвятся (или хотя бы один зареджектится, но это не наш случай), и резолвится массивом результатов.

Разумеется, все дальнейшие действия ты можешь делать только внутри функции then, ну или использовать этот промис для построения других конструкций из промисов.

Например, если надо что-то грузить после этих картинок, делай так
getArray(test).then(function(arr) {
   return otherGetDataPrimise(arr);
}).then(function(data) {
  // здесь data - то что было загружено
});


если параллельно с картинками, то вот так
Promise.all([
  getArray(test),
  otherGetDataPrimise()
]).then(function(data) {
  // data - массив. data[0] - массив картинок, data[1] - данные, полученные otherGetDataPrimise
});


В общем, можно произвольно комбинировать. Ты можешь прямо на листочке нарисовать схему, что и в каком порядке должно грузиться, оформить подгрузку каждой части как отдельную функцию, возвращающую промис, и потом легко и непринужденно собрать из них схему любой сложности. Эта схема опять же будет возвращать промис, который зарезолвится полным набором результатов, и в его then ты выполнишь финальные действия с полученными данными.

Обработку ошибок можно навтыкать по ситуации. getArray из моего предыдущего поста (и функции внутри него) не реджектится, из предположения, что если хотя бы часть картинок загрузилось - "и то хлеб". А если надобно по схеме "все или ничего", то просто реджектишь в reader.onerror, и весь getArray вылетает в ошибку.

Prowler 07.04.2017 10:52

Alexandroppolus,
Ну да, я это и имел ввиду говоря о цепочках. Т.к. с темой только познакомился, подумал (ну мало ли!), что может быть есть какой то механизм в них который каким-либо образом синхронизирует выполнение в функции.... ну что-то вроде невозможного:
function myFunc(){
//собираем данные с первой формы
//собираем данные со второй формы 
Promise.all().then(); //сгребаем картинки
//продолжаем сбор данных со второй формы
//а теперь с третьей формы
}


Ну да, чудес не бывает. Нагородим цепочку. Благодарю


Часовой пояс GMT +3, время: 19:32.