Как реализовать индикатор выполнения и обратные вызовы с асинхронным характером FileReader

У меня есть API FileReader, вызываемый в цикле for для перебора нескольких файловых объектов. Я использую FileReader для предварительного просмотра изображений.

function() {
    for (var i in Files) {
        var fileReader = new FileReader();
        fileReader.readAsBinaryString(Files[i]);
        fileReader.onload = function() {

            // do something on FileReader onload

        }

        fileReader.onprogress = function(data) {
            if (data.lengthComputable) {                                            
                var progress = parseInt( ((data.loaded / data.total) * 100), 10 );
                console.log(progress);
            }
        }
    }

    // do something on completion of FileReader process
    // actions here run before completion of FileReader
}

Я столкнулся с двумя проблемами из-за асинхронного характера API FileReader. Во-первых, событие onprogress запускается для каждого экземпляра FileReader. Это дает мне прогресс для каждого файла. Принимая во внимание, что я намерен отображать общий прогресс для всех файлов, а не для отдельных файлов.

Во-вторых, я хочу выполнять действия, которые должны выполняться только после завершения всех экземпляров (по одному для каждого файла) FileReader. В настоящее время, поскольку FileReader работает асинхронно, действия выполняются до того, как FileReader завершит свою задачу. Я много искал, но пока не нашел решения этих проблем. Любая помощь приветствуется.


person John    schedule 08.05.2013    source источник
comment
Использование parseInt для чисел является анти-шаблоном. Если вы хотите округлить (или обрезать) значение (data.loaded / data.total) * 100, используйте Math.round или Math.floor. Вы также можете сначала выполнить часть * 100, чтобы промежуточное значение не было таким уж маленьким (поскольку это может помочь предотвратить потерю точности): var progress = Math.round((data.loaded * 100) / data.total);   -  person T.J. Crowder    schedule 17.11.2020


Ответы (1)


Давайте сначала решим вашу вторую проблему. Вам нужно определить свой код после завершения в отдельной функции и вызвать эту функцию после загрузки всех файлов:

function() {
    var total = Files.length; loaded = 0;
    for (var i in Files) {
        var fileReader = new FileReader();
        fileReader.readAsBinaryString(Files[i]);
        fileReader.onload = function() {

            // do something on FileReader onload
            loaded++;

            if (loaded == total){
                onAllFilesLoaded();
            }
        }

        fileReader.onprogress = function(data) {
            if (data.lengthComputable) {                                            
                var progress = parseInt( ((data.loaded / data.total) * 100), 10 );
                console.log(progress);
            }
        }
    }
}

function onAllFilesLoaded(){
    //do stuff with files
}

Теперь, для отслеживания прогресса, есть несколько способов решить эту проблему. Прямо сейчас вы загружаете все файлы одновременно, и каждый файл сообщает о своем прогрессе. Если вы не возражаете против менее частых обновлений прогресса, вы можете просто использовать обработчик onload, чтобы сообщать о прогрессе каждый раз, когда файл загружается. Если вам нужны действительно детализированные и точные обновления прогресса, вам придется рассчитать общий размер всех объединенных файлов, а затем отслеживать, сколько загружено каждого файла, и использовать сумму загруженного для каждого файла. файла по сравнению с общим размером всех файлов, чтобы сообщить о ходе выполнения.

В качестве альтернативы, предполагая, что вы реализуете это с помощью индикатора выполнения, а не console.log, вы можете предоставить отдельный индикатор выполнения для каждого загружаемого файла и вычислить прогресс для каждого файла точно так же, как вы делаете это сейчас, а затем обновить соответствующий индикатор выполнения. Дайте мне знать, если что-то из этого нуждается в разъяснении.

person undefined    schedule 08.05.2013
comment
Всего несколько минут назад я реализовал это же решение и вернулся к этому вопросу, чтобы обнаружить, что вы предложили именно то, что я реализовал. Для прогресса мне не нужны детализированные значения. В итоге я вычислил прогресс как var progress = parseInt( ((loaded / totalNumberOfFiles) * 100), 10 );. Спасибо за помощь. - person John; 08.05.2013
comment
отлично, надеюсь, это поможет и нескольким другим. - person undefined; 08.05.2013