TL;DR
monkey-patch.ts
interface HTMLImageElement {
asyncLoad(): Promise<void>
}
// jsなら↓だけでおk
HTMLImageElement.prototype.asyncLoad = function() {
if (this.complete) return Promise.resolve()
return new Promise(resolve => {
this.addEventListener("load", function callback() {
resolve(this)
this.removeEventListener("load", callback)
})
})
}
if (this.complete) return Promise.resolve()
を記述しないと、読み込み済みの場合 Promise が解決されないので注意
概要
全ての img 要素が読み込み終わったら特定のアクションを実行するとか、new Image()
したやつを読み込み終わるまで await したりなどでわざわざ関数書くのも面倒なんで、モンキーパッチと呼ばれるテクニックで HTMLImageElement に asyncLoad というメソッドを追加してみました。
※ Effective JavaScriptにやみくもなモンキーパッチは避けるべきと書いてあるので、業務で使用する場合は上の確認を取ってから実装するように!
使用例
import "./monkey-patch.ts"
/** img要素の全てが読み込まれた場合Promiseが解決される*/
const asyncLoadAllImages = async () => {
const promises = Array.from(document.querySelectorAll("img"), el => el.asyncLoad())
await Promise.all(promises)
}
/** 特定パスから画像を読み込まれた場合Promise<HTMLImageElement>が解決される */
const asyncGetImage = async (src: string) => {
const image = new Image()
image.src = src
await image.asyncLoad()
return image
}
余談
Promise を返却するような非同期関数の命名規則ってどうなっているんだろう。。
基本的に関数名先頭に async を付けてるけど正直コレジャナイ感がすごい。。
あと、「Promise が解決される」って言い方はMDNにそって言ってるけど、これも日本語的にすごく違和感...🤔