🤖
js で esmodule下であるならば top level await が有効となっているのだから無限ループで 処理してもいいよね
というわけで javascript のアイディアの話です。 setTimeout でループを書いている旧世代の処理を 無限ループで書いてみようという話となります。
setTimeout のループ
具体的にはこれまではこう書いていたところを
function loopAction() {
// doing
setTimeout(loopAction, 500);
}
loopAction();
こう書いてみませんか?
while(true) {
// doing
await new Promise(resolve => setTimeout(resolve, 500));
}
という話です。
これはいろいろと応用が利きまして
画像の読み込み
画像の読み込みを こう書いていたものを
const srcUrl = "画像のファイルパス";
const image = new Image();
image.addEventListener('error', ({error}) => {
// doing error process
});
Image.addEventListener('load',({target:image}) => {
// doing load complete process
}
Image.src = srcUrl;
こう書くことができます。
const srcUrl = "画像のファイルパス";
try {
const image = await new Promise((resolve, reject) => {
const image = new Image();
image.addEventListener('error', ({error}) => reject(error));
image.addEventListener('load', ({target}) => resolve(target));
image.src = srcUrl;
});
// doing load complete process
} catch (error) {
// doing error process
}
この Image を使いまわししたいとなるなら次の様に書いたらいいと思います。
addEventListener の options に signal を指定すると abort 時に解除してくれます。
const srcUrl = "画像のファイルパス";
try {
const controller = new AbortController();
let image;
try {
const signal = controller.signal;
image = await new Promise((resolve, reject) => {
signal.throwIfAborted()
signal.addEventListener('abort', ({target}) => reject(target.reason));
const image = document.getElementById('使いまわす image の id');
image.addEventListener('error', ({error}) => reject(error), {signal});
image.addEventListener('load', ({target}) => resolve(target), {signal});
image.src = srcUrl;
});
} finally {
controller.abort();
}
// doing load complete process
} catch (error) {
// doing error process
}
これによりイベントは処理後にちゃんと解除までされます。
もしも let image が気持ち悪いのであれば次の様に書いても同様の動作となります。
const srcUrl = "画像のファイルパス";
try {
const controller = new AbortController();
const signal = controller.signal;
const image = await new Promise((resolve, reject) => {
signal.throwIfAborted()
signal.addEventListener('abort', ({target}) => reject(target.reason));
const image = document.getElementById('使いまわす image の id');
image.addEventListener('error', ({error}) => reject(error), {signal});
image.addEventListener('load', ({target}) => resolve(target), {signal});
image.src = srcUrl;
}).finally(() => controller.abort());
// doing load complete process
} catch (error) {
// doing error process
}
Promise の finally は戻り値を変更せずに resolve / reject どっちの場合でも処理されるメソッドな為、こういう際に利用できます。
ファイルの読み込み
ファイルの読み込みも昔は下記の様に書いていたのですが
const file = event.target.files[0];
const reader = new FileReader();
reader.addEventListener('error', ({target: {error}}) => {
// doing error process
});
reader.addEventListener('load', ({target: {result}}) => {
// doing load complete process
});
reader.readAsArrayBuffer();
今ならこう書けます。
const file = event.target.files[0];
try {
const result = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener('error', ({target: {error}}) => reject(error));
reader.addEventListener('load', ({target: {result}}) => resolve(result));
reader.readAsArrayBuffer();
});
// doing load complete process
} catch (error) {
// doing error process
}
いえ、 そもそも Blob に arrayBuffer() が生えているので これでいいです。
const file = event.target.files[0];
try {
const result = await file.arrayBuffer();
// doing load complete process
} catch (error) {
// doing error process
}
以上。
Discussion