こんにちは。
皆さんもFileReaderを使う機会は多いと思います。
FileReaderは非同期なので、コールバック地獄に陥りがちです。
しかし最近のJavaScriptには、非同期なコードを同期的に扱えるasync/awaitという素晴らしい機能が存在します。
FileReaderもasync/awaitの恩恵を受けたい!!
解決策
class FileReaderEx extends FileReader{
constructor(){
super();
}
#readAs(blob, ctx){
return new Promise((res, rej)=>{
super.addEventListener("load", ({target}) => res(target.result));
super.addEventListener("error", ({target}) => rej(target.error));
super[ctx](blob);
});
}
readAsArrayBuffer(blob){
return this.#readAs(blob, "readAsArrayBuffer");
}
readAsDataURL(blob){
return this.#readAs(blob, "readAsDataURL");
}
readAsText(blob){
return this.#readAs(blob, "readAsText");
}
}
元のFileReaderクラスをPromiseでラップした拡張クラスを作成して万事解決。
#readAs()
は外部から見られたくないのでプライベートメソッドにしてあります。
ビフォーアフター
今までコールバックにまみれていたコードも...
Before
const reader1 = new FileReader();
reader1.addEventListener("load", ()=>{
const buffer1 = reader1.result;
const reader2 = new FileReader();
reader2.addEventListener("load", ()=>{
const buffer2 = reader2.result;
const reader3 = new FileReader();
reader3.addEventListener("load", ()=>{
const buffer3 = reader3.result;
});
reader3.addEventListener("error", ()=>{
alert(reader3.error.message);
});
reader3.readAsArrayBuffer(blob3);
});
reader2.addEventListener("error", ()=>{
alert(reader2.error.message);
});
reader2.readAsArrayBuffer(blob2);
});
reader1.addEventListener("error", ()=>{
alert(reader1.error.message);
});
reader1.readAsArrayBuffer(blob1);
async/awaitの手に掛かればこの通り!!
After
(async()=>{
const buffer1 = await new FileReaderEx().readAsArrayBuffer(blob1);
const buffer2 = await new FileReaderEx().readAsArrayBuffer(blob2);
const buffer3 = await new FileReaderEx().readAsArrayBuffer(blob3);
})();
一目瞭然で見やすくなっています。
FileReader以外にも応用が利く、かなりオススメな手法です。