Страницы

Поиск по вопросам

среда, 19 июня 2019 г.

Контроль порядка выдачи результатов асинхронной операции

Я пишу программу, которая показывает превью изображений, добавляя их в ячейки таблицы. В соседних ячейках есть поля для имени, тегов и источника загружаемых фотографий, которые я также заполняю, после чего загружаю всю эту информацию вместе с файлами на сервер. Я использую объект FileReader, который в свою очередь записывает файлы методом readAsDataUrl. Но проблема в том, что этот метод работает асинхронно и как результат я вижу превью не в том порядке, в котором файлы будут загружены на сервер, а сами превью не соответствуют файлам, которые за ними стоят, получается перемешка. Это породило проблему, поскольку описания файла, которые вносит пользователь (имя, тег, источник) в результате не соответствуют картинке.
Вот как это выглядит (обратите внимание на описания). Процесс загрузки:

Результат загрузки:

Здесь люди уже поднимали и решили похожую проблему, но я не знаю, как применить эти идеи в моем случае, когда для каждого превью динамически создаются элементы таблицы. Прошу помощи.
Мой код:
JavaScript
$(document).ready(function () {
$("#fileUpload").on('change', function () {
//Get count of selected files var countFiles = $(this)[0].files.length; var imgPath = $(this)[0].value; var extn = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase(); var image_holder = $("#image-holder"); image_holder.empty(); if (extn == "gif" || extn == "png" || extn == "jpg" || extn == "jpeg") { if (typeof(FileReader) != "undefined") { //loop for each file selected for uploaded.
var newElem = document.createElement('table'); newElem.id = 'tl'; newElem.align = 'center'; newElem.border = 0;
for (var i = 0; i < countFiles; i++) { var reader = new FileReader();
reader.onload = function (e) {
var newRow = newElem.insertRow(0); var newCell1 = newRow.insertCell(0); newCell1.innerHTML = ""; var newCell2 = newRow.insertCell(0); newCell2.innerHTML = ""; var newCell3 = newRow.insertCell(0); newCell3.innerHTML = ""; var newCell4 = newRow.insertCell(0);
$("", { "src": e.target.result, "class": "thumb-image" }).appendTo(newCell4);
};
document.getElementById("image-holder").appendChild(newElem); reader.readAsDataURL($(this)[0].files[i]); image_holder.show(); } } else { alert("This browser does not support FileReader."); } } else { alert("Please select images only"); } }); });
HTML:




Ответ

Для организации последовательной загрузки можно использовать Promise
Для этого загрузку изображения с помощью FileReader, можно вынести в функцию, которая будет возвращать Promise, который разрешится тогда, когда картинка загрузится.
Она может выглядеть так:
function loadImage(image){ return new Promise(function(resolve, reject){ var fileReader = new FileReader(); fileReader.onload = function(e){ resolve(e.target.result); } fileReader.readAsDataURL(image); }); }
Используя функцию then можно подписаться на событие, которое произойдет, когда Prmoise перейдет в состояние готов.
например так:
loadImage().then(function(imageAsDataUrl){ ... });
Таким образом можно собрать цепочку асинхронных операций, которые будут выполняться друг за другом.
Итоговый вид может быть таким:
var queue = Promise.resolve();
[].reduce.call(this.files,function(queue, file, index){ return queue.then(function(){ return loadImage(file).then(function(imageAsDataUrl){ var newRow = newElem.insertRow(0); var newCell1 = newRow.insertCell(0); newCell1.innerHTML = ""; var newCell2 = newRow.insertCell(0); newCell2.innerHTML = ""; var newCell3 = newRow.insertCell(0); newCell3.innerHTML = ""; var newCell4 = newRow.insertCell(0);
$("", { "src": imageAsDataUrl, "class": "thumb-image" }).appendTo(newCell4); }); }); }, Promise.resolve()).then(function(){ // все картинки загрузились image_holder.show(); })
небольшое отступление: $(this)[0] это то же самое, что и this, можете проверить с помощью следующего выражения $(this)[0] === this вернет true

Комментариев нет:

Отправить комментарий