2015-12-28 3 views
5

я следующий HTML код:Javascript Обещания с FileReader()

<input type='file' multiple> 

А вот мой JS код:

var inputFiles = document.getElementsByTagName("input")[0]; 
inputFiles.onchange = function(){ 
    var fr = new FileReader(); 
    for(var i = 0; i < inputFiles.files.length; i++){ 
     fr.onload = function(){ 
      console.log(i) // Prints "0, 3, 2, 1" in case of 4 chosen files 
     } 
    } 
    fr.readAsDataURL(inputFiles.files[i]); 
} 

Так что мой вопрос, как я могу сделать эту петлю синхронно? Прежде всего, подождите, пока файл завершит загрузку, а затем перейдет к следующему файлу. Кто-то сказал мне использовать JS Promises. Но я не могу заставить его работать. Вот что я хочу:

var inputFiles = document.getElementsByTagName("input")[0]; 
inputFiles.onchange = function(){ 
    for(var i = 0; i < inputFiles.files.length; i++){ 
     var fr = new FileReader(); 
     var test = new Promise(function(resolve, reject){ 
      console.log(i) // Prints 0, 1, 2, 3 just as expected 
      resolve(fr.readAsDataURL(inputFiles.files[i])); 
     }); 
     test.then(function(){ 
      fr.onload = function(){ 
       console.log(i); // Prints only 3 
      } 
     }); 
    }; 
} 

Заранее спасибо ...

+3

ОБЕЩАНИЯ используются для асинхронных операций. –

+0

Как это сделать синхронно? Я учился в Интернете, все говорят, что он делает код синхронным –

+0

@ZahidSaeed: Нет, обещания не делают код синхронным. Можете ли вы указать на одно из тех мест, которые «все» говорят, что они делают? –

ответ

4

Характер FileReader является то, что вы не можете сделать его работу синхронно.

Я подозреваю, что вам действительно не нужно или хотите, чтобы он был синхронным, просто чтобы вы правильно получили результирующие URL-адреса. Если это так, я бы не подумал, что обещания действительно помогут. Вместо этого, просто следить за сколько выдающего операций вы, так что вы знаете, когда вы сделали:

var inputFiles = document.getElementsByTagName("input")[0]; 
inputFiles.onchange = function(){ 
    var data = [];  // The results 
    var pending = 0; // How many outstanding operations we have 

    // Schedule reading all the files (this finishes before the first onload 
    // callback is allowed to be executed) 
    Array.prototype.forEach.call(inputFiles.files, function(file, index) { 
     // Read this file, remember it in `data` using the same index 
     // as the file entry 
     var fr = new FileReader(); 
     fr.onload = function() { 
      data[index] = fr.result; 
      --pending; 
      if (pending == 0) { 
       // All requests are complete, you're done 
      } 
     } 
     fr.readAsDataURL(file); 
     ++pending; 
    }); 
} 

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

// Note: This assumes there is at least one file, if that 
// assumption isn't valid, you'll need to add an up-front check 
var inputFiles = document.getElementsByTagName("input")[0]; 
inputFiles.onchange = function(){ 
    var index = 0; 

    readNext(); 

    function readNext() { 
     var file = inputFiles.files[index++]; 
     var fr = new FileReader(); 
     fr.onload = function() { 
      // use fr.result here 
      if (index < inputFiles.files.length) { 
       // More to do, start loading the next one 
       readNext(); 
      } 
     } 
     fr.readAsDataURL(file); 
    } 
} 
9

Если вы хотите сделать это последовательно (не синхронно) с помощью Promises, вы могли бы сделать что-то вроде:

var inputFiles = document.getElementsByTagName("input")[0]; 
inputFiles.onchange = function(){ 
    var promise = Promise.resolve(); 
    inputFiles.files.map(file => promise.then(()=> pFileReader(file))); 
    promise.then(() => console.log('all done...')); 
} 

function pFileReader(file){ 
    return new Promise((resolve, reject) => { 
    var fr = new FileReader(); 
    fr.onload = resolve; // CHANGE to whatever function you want which would eventually call resolve 
    fr.readAsDataURL(file); 
    }); 
} 
+0

ты мужчина! Вторая часть (новая Promise) - это именно то, что мне нужно, чтобы заставить FileReader возвращаться как обещание по мере необходимости. Спасибо! – skplunkerin

+0

Я должен был выполнить 'new Promise ((разрешить, отклонить) => {', чтобы заставить это работать ... (Нужно '' generic.) – Vaccano

1

Мы модифицировали midos ответить, чтобы заставить его работать следующим образом:

function readFile(file){ 
    return new Promise((resolve, reject) => { 
    var fr = new FileReader(); 
    fr.onload =() => { 
     resolve(fr.result) 
    }; 
    fr.readAsText(file.blob); 
    }); 
} 
-1

Вот еще одна модификация ответа Йенса (Спекуляция от ответа Мидо), чтобы дополнительно проверить размер файла:

function readFileBase64(file, max_size){ 
     max_size_bytes = max_size * 1048576; 
     return new Promise((resolve, reject) => { 
      if (file.size > max_size_bytes) { 
       console.log("file is too big at " + (file.size/1048576) + "MB"); 
       reject("file exceeds max size of " + max_size + "MB"); 
      } 
      else { 
      var fr = new FileReader(); 
      fr.onloadend =() => { 
       data = fr.result; 
       resolve(data) 
      }; 
      fr.readAsDataURL(file); 
      } 
     }); 
    } 
Смежные вопросы