Javascript Promises with FileReader()

javascript filereader
javascript filereader async
javascript filereader wait for result
javascript filereader synchronous
javascript filereader onload function
filereader example
javascript filereader blocking
file reader return promise

I have the following HTML Code:

<input type='file' multiple>

And Here's my JS Code:

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

So my question is, how can I make this loop synchronous ? That is first wait for the file to finish loading then move on to the next file. Someone told me to use JS Promises. But I can't make it to work. Here's what I'm trying:

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

Thanks in advance...

If you want to do it sequentially( not synchronously) using Promises, you could do something like:

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

Javascript Promises with FileReader(), The nature of FileReader is that you cannot make its operation synchronous. Then you'd use the promise-based operations I describe in this answer to read those in parallel: Promise. all(Array. This function returns a Promise of a string that will resolve or reject only after the firing of the.onload and.onerror event handlers, respectively. If the Promise is rejected the FileReader will abort and, in this case, return a custom DOMException but you can reject with whatever you like.

We modified midos answer to get it to work the following:

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

FileReader Promise example, const temporaryFileReader = new FileReader();. 3. . 4. return new Promise(( resolve, reject) => {. 5. temporaryFileReader.onerror = () => {. 6. The FileReader API can be used to read a file asynchronously in collaboration with JavaScript event handling. However, all the browsers do not have HTML 5 support so it is important to test the browser compatibility before using the File API. There are four inbuilt methods in the FileReader API to read local files:

The nature of FileReader is that you cannot make its operation synchronous.

I suspect you don't really need or want it to be synchronous, just that you want to get the resulting URLs correctly. The person suggesting using promises was probably right, but not because promises make the process synchronous (they don't), but because they provide standardized semantics for dealing with asynchronous operations (whether in paralle or in series):

Using promises, you'd start with a promise wrapper for readAsDataURL (I'm using ES2015+ here, but you can convert it to ES5 with a promise library instead):

function readAsDataURL(file) {
    return new Promise((resolve, reject) => {
        const fr = new FileReader();
        fr.onerror = reject;
        fr.onload = function() {
            resolve(fr.result);
        }
        fr.readAsDataURL(file);
    });
}

Then you'd use the promise-based operations I describe in this answer to read those in parallel:

Promise.all(Array.prototype.map.call(inputFiles.files, readAsDataURL))
.then(urls => {
    // ...use `urls` (an array) here...
})
.catch(error => {
    // ...handle/report error...
});

...or in series:

Array.prototype.reduce.call(inputFiles.files, (p, file) => p.then(() => readAsDataURL(file).then(url => {
    // ...use `url` here...
})), Promise.resolve())
.catch(error => {
    // ...handle/report error...
});

Inside an ES2017 async function, you could use await. It doesn't do much more the parallel version:

// Inside an `async` function
try {
    const urls = await Promise.all(Array.prototype.map.call(inputFiles.files, readAsDataURL));
} catch (error) {
    // ...handle/report error...
}

...but it makes the series version much simpler and clearer:

// Inside an `async` function
try {
    for (let i = 0; i < inputFiles.files.length; ++i) {
        const file = inputFiles.files[i];
        const url = await readAsDataURL(file);
        // ...use `url` here...
    }
} catch (error) {
    // ...handle/report error...
}

Without promises, you'd do this by keeping track of how many outstanding operations you have so you know when you're done:

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

Or if you want for some reason to read the files sequentially (but still asynchronously), you can do that by scheduling the next call only when the previous one is complete:

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

Read Local Files in JavaScript with FileReader, The FileReader class lets you read files from a native file input. The FileReader class' async API isn't ideal for usage with async/await or promise chaining. With the above readFile() helper, you can read a file in an async� FileReader objects can read from a file or a blob, in one of three formats: String (readAsText). ArrayBuffer (readAsArrayBuffer). Data url, base-64 encoded (readAsDataURL). In many cases though, we don’t have to read the file contents. Just as we did with blobs, we can create a short url with URL.createObjectURL(file) and assign it to <a> or

I upgrade Jens Lincke answer by add working example and introduce async/await syntax

function readFile(file) {
  return new Promise((resolve, reject) => {
    let fr = new FileReader();
    fr.onload = x=> resolve(fr.result);
    fr.readAsDataURL(file) // or readAsText(file) to get raw content
})}

function readFile(file) {
  return new Promise((resolve, reject) => {
    let fr = new FileReader();
    fr.onload = x=> resolve(fr.result);
    fr.readAsDataURL(file) // or readAsText(file) to get raw content
})}

async function load(e) {
  for(let [i,f] of [...e.target.files].entries() ){
    msg.innerHTML += `<h1>File ${i}: ${f.name}</h1>`;
    let p = document.createElement("pre");
    p.innerText += await readFile(f);
    msg.appendChild(p);
  }
}
<input type="file" onchange="load(event)" multiple />
<div id="msg"></div>

Using the FileReader API in async functions • Simon Schraeder , The FileReader API allows you to read files from the users' computer. Due to readAsArrayBuffer() function as it allows you to selectively read the file and only store part of it in memory. function readFileAsync(file) { return new Promise(( resolve, reject) => { let reader = new JavaScript 1.7; HTML; Result. let promise = Promise.all([ promises ]); Promise.all takes an array of promises (it technically can be any iterable, but is usually an array) and returns a new promise. The new promise resolves when all listed promises are settled, and the array of their results becomes its result.

Here is another modification to Jens' answer (piggybacking off Mido's answer) to additionally check the file size:

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.onload = () => {
                data = fr.result;
                resolve(data)
            };
            fr.readAsDataURL(file);
            }
        });
    }

promise-file-reader, Miss any of our Open RFC calls?Watch the recordings here! � promise-file- reader. 1.0.2 • Public • Published 2 years ago. Readme � ExploreBETA � 0� Interactive API reference for the JavaScript FileReader Object. FileReader is used to read the contents of a Blob or File.

JavaScript: Wrapping the FileReader API into a new one that uses , The FileReader API relies on callbacks to maintain asynchronous I wrap the FileReader API Duration: 29:56 Posted: Sep 25, 2019 FileReader is well supported in modern browsers, as well as IE10. Note that FileReader is a browser API, so, while most browsers support it, FileReader is not part of Node.js. With Promises and Async/Await. The FileReader class' async API isn't ideal for usage with async/await or promise chaining. Here's how you can wrap a FileReader in a

Read a File using a FileReader returning a jQuery promise � GitHub, Read a File using a FileReader returning a jQuery promise. gistfile1.js var reader = new FileReader(); And how to use the Promise.all() in filereader()?.

FileReader Promise Helper � GitHub, FileReader Promise Helper. index.js. const readFile = (file) => {. let reader = new global.FileReader();. return new Promise((resolve, reject) => {. reader.onload�

Comments
  • Promises are used for asynchronous operations.
  • How do I make it synchronous then ? I have studied on internet, they all say that it makes the code synchronous
  • @ZahidSaeed: No, promises don't make code synchronous. Can you point to one of those places that "all" say they do?
  • MDN Says: "A Promise represents a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers to an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like SYNCHRONOUS METHODS":
  • Yes. You can make it behave in a synchronous fashion if by synchronous you mean that you can control the order in which the different parts of your file reading code are executed. But it will still run asynchronously in respect to e.g. mouse handlers or the JavaScript engine itself. "like a synchronous method" is not the same as "is a synchronous method".
  • you're the man! The 2nd part (new Promise) is exactly what I needed to get my FileReader to return as a Promise as needed. Thank you!
  • I had to do new Promise<Event>((resolve, reject) => { to get this to work... (Needed the <Event> generic.)
  • dude. can you update your answer with just how to execute the pFileReader(file) and get the result from promise in a variable instead of inputfiles.files.map because am new to this and struggling with the solution. but your sol looks great. what i am trying to do is to call a method to convert image to base64 and return it to the caller and i save the data into a variable. pls help
  • I tried A LOT of different solutions and this was the only one that worked for me calling an API from Blazor. Thank you!!!
  • This answer correctly uses onloadend instead of onload which may give partial results.
  • ?? No, I was saying that onloadend is the right one to use. Every other answer uses the wrong api (onload)
  • onloadend triggers if the file was read successfully or unsuccessfully, while onload triggers only on successful read @nathan-m