Understanding FileReader

The FileReader API is event driven. You define custom behavior in its event handlers when it either succeeds or fails to read a file and then pass a File object to one of the methods, such as readAsText The result of the method, if successful, can finally be retrieved in the .result property.

The FileReader methods work asynchronously but don't return a Promise. And attempting to retrieve the result immediately after calling a method will not work, as the .onload event handler fires only after the FileReader has successfully finished reading the file and updates the FileReader's .result property. This can make FileReader a bit of a pain to work with when integrating it with an application.

Wrapping it With a Promise

A solution to make FileReader more pleasant to work with is to wrap its result in a Promise. Here's an example:

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. One good alternative is the FileReader's .error property, which will also be a DOMException.

A Simplified API with Async/Await

Wrapping the result of a FileReader in a Promise allows us to await the result of our readUploadedFileAsText function and expect a string. Here's an example onchange handler we could pass to a <input type="file /> element (Full example on CodePen)

React Examples

The CodePen link above shows the above code doing manual DOM manipulation. Here's an example of using the same readUploadedFileAsText function with async/await in a React application.

For an example in a TypeScript and React application, look here. Though this particular code example is part of an application using Redux so there's a fair bit of indirection.