2015-12-28 14 views
5

Mam następujący kod HTML:JavaScript Promises z FileReader()

<input type='file' multiple> 

i tu jest mój kod 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]); 
} 

Więc moje pytanie brzmi, Jak mogę dokonać tej pętli synchroniczne? To jest najpierw czekać na zakończenie ładowania pliku, a następnie przejść do następnego pliku. Ktoś powiedział mi, żebym użył JS Promises. Ale nie mogę sprawić, żeby działało. Oto, co usiłuję:

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

Z góry dzięki ...

+3

obiecuje są używane do operacji asynchronicznych. –

+0

Jak mogę to zrobić synchronicznie? Nauczyłem się w Internecie, wszyscy mówią, że sprawia, że ​​kod synchroniczny –

+0

@ZahidSaeed: Nie, obietnice nie powodują synchronizacji kodu. Czy możesz wskazać jedno z tych miejsc, które "wszyscy" mówią? –

Odpowiedz

4

Charakter FileReader jest to, że nie można dokonać jego bieg synchroniczny.

Podejrzewam, że tak naprawdę nie potrzebujesz lub chcesz, aby był synchroniczny, tylko że chcesz uzyskać wynikowe adresy URL poprawnie. Jeśli tak, nie sądzę, by obietnice naprawdę pomogły. Zamiast tego, po prostu śledzić ilu wybitny operacji masz tak wiesz, kiedy skończysz:

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

Lub jeżeli chcesz z jakiegoś powodu do odczytu plików kolejno (ale nadal asynchronicznie), można zrobić, planując kolejną rozmowę tylko wtedy, gdy poprzedni jest kompletna:

// 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

Jeśli chcesz to zrobić po kolei (nie synchronicznie) używając Promises, można zrobić coś takiego:

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

jesteś mężczyzną! Druga część (nowa obietnica) jest dokładnie tym, czego potrzebowałem, aby mój FileReader powrócił jako Promise w razie potrzeby. Dziękuję Ci! – skplunkerin

+0

Musiałem zrobić 'nową obietnicę ((rozwiązać, odrzucić) => {', aby to zadziałało ... (Potrzebowałem 'generatora '.) – Vaccano

1

Mamy zmodyfikowane midos odpowiedzieć zmusić go do pracy, co następuje:

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

Oto kolejna modyfikacja odpowiedzi Jensa (piggybacking off odpowiedź Mido), aby dodatkowo sprawdzić rozmiar pliku:

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); 
      } 
     }); 
    } 
Powiązane problemy