JavaScriptで画像とか読み込む時・・
▼ こういうコードを書いたりする
1 2 3 4 5 |
var image = new Image(); image.onload = function(){ /// 読込後の処理... } image.src = '画像パス'; |
この読込には次の問題点があります。
- 外側で非同期な読み込みになる
- ネストが無駄に長くなる
こういうのが少しイヤですね。
そこで画像を同期的に読み込む方法を紹介!
画像に限らず汎用的に使える方法です。
同期的に読み込むための2つの前提知識
まず1つめは Promise を使うこと。
▼ Promiseとは何か。MDNでの解説
Promise インターフェイスは作成時点では分からなくてもよい値へのプロキシです。 Promise を用いることで、非同期アクションの成功や失敗に対するハンドラーを関連付けることができます。これにより、非同期メソッドは、最終的な値を返すのではなく、未来のある時点で値を持つ Promise を返すことで、同期メソッドと同じように値を返すことができるようになります。
引用元 : https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
▼ Promiseの状態について...
Promise の状態は以下のいずれかとなります。
待機pending: 初期状態。成功も失敗もしていません。
満足fulfilled: 処理が成功して完了したことを意味します。
拒絶rejected: 処理が失敗したことを意味します。
この解説で大事と考えられるのが、
「作成時点では分からなくてもよい値」
この部分ですね。
これを応用することで Ajax などの非同期処理が実現されている訳です。XMLHttpRequest を使うより簡単でスマートなコードが書けて便利です。
もう1つが async関数 と await演算子
▼ async関数とは何かのMDNの説明
async function 宣言は、 非同期関数 AsyncFunction オブジェクトである関数を定義します。非同期関数はイベントループを介して他のコードとは別に実行され、結果として暗黙の Promise を返します。ただし、非同期関数を使用したコードの構文および構造は、通常の同期関数と似たものになります。
引用元 : https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function
▼ await演算子についてのMDNの説明
await 演算子は、async function によって Promise が返されるのを待機するために使用します。
引用元 : https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/await
関数に対して async function hoge(){...} など宣言すると、その関数は非同期に実行されます。つまり呼び出し時点では即実行されないということ。ただ返り値を確実に受け取るために Promise が返されるみたいです、
一方の await演算子 は非同期関数によって返された Promise が「完了されるまで待つ」ための演算子ですね。まさしく文字通りで分かりやすい。
この2つの仕組みを合わせて使えばOKです。
1つの画像を同期的に読み込むコードの書き方
まずは1つの画像だけを同期的に読込
そのために次のコードを書いてみました。
▼ このようなコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/// 1.Promiseを使った同期読み込み async function loadImage(imgUrl){ var img = null; var promise = new Promise(function(resolve){ img = new Image(); img.onload = function(){ /// 読み込み完了後... console.log('loaded : '+imgUrl); resolve(); } /// 読み込み開始... img.src = imgUrl; }); /// 読込完了まで待つ await promise; return img; } /// 画像読み込みするasyncな関数 async function main(){ var img1 = await loadImage('img1.jpg'); console.log('img1 : ', img1); } |
▼ このコードのコンソール出力
1 2 |
loaded : img1.jpg img1 : <img src="img1.jpg"> |
コードだけだと「本当に同期的か?」と疑ってしまいますが、コンソール出力すると分かりやすいです。 img.onload = function(){...} 内部が呼ばれた後に返却されているのが見て取れます。
複数画像を一気に同期読み込みするコード
コードだけ載せると次の通りです。
▼ このようなコードを書いてみた
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
async function loadImages(imgUrls){ const promises = []; const images = []; imgUrls.forEach(function(url){ var promise = new Promise(function(resolve){ const img = new Image(); img.onload = function(){ console.log('loaded : '+url); resolve(); } img.src = url; images.push(img) }); promises.push(promise); }); /// すべて読込完了まで待つ await Promise.all(promises); return images; } /// 画像読み込みするasyncな関数 async function main(){ var images = await loadImages([ 'img1.jpg', 'img2.png' ]); console.log('images : ', images); } |
このコードでは promises に対して返却対象の Promise を代入していき、 await Promise.all(promises); することで全Promiseが解決されるまで待ってるだけです。
複数画像の同期読み込むも難しくありません。
以上、JSでの画像同期読み込みでした。
もちろん画像以外でも色々応用が効きます。
もし間違いがあればご指摘ください。ではまた