img要素のonloadを動的に設定する際の挙動について

投稿者: Anonymous

全ての<img>のロードが完了した時点で処理をしたい、といった質問の回答において、$.Deferredを使った例が挙げられています。
(以下 https://ja.stackoverflow.com/questions/6086#comment5088_6127 よりコードを引用)

$.fn.all_loaded = function () {
    var all_done = new Array();
    this.each(function () {
        if ( this.complete || this.readyState === 4) 
           return;
        var promise = new $.Deferred();
        all_done.push(promise);
        this.onload = function () {
            promise.resolve();
        }
    });
    return $.when.apply($, all_done);
};

$('.images').each( function(i, holder) {
    $(holder).find('img').all_loaded().done(function() {
        if( holder.onload) holder.onload.call( holder);
    });
});

このコードではcompletereadyStateを用いたロード済みチェックを行った後にonloadイベントの設定を行っていますが、この間にロードが完了することでonloadが呼ばれない可能性はないのでしょうか。

試しにonloadイベントを設定する直前にダミーループを仕込んで時間をかけさせてみたところ、きちんとイベントは発火しているようなのですが、規格上の根拠がないものかと探しています。

// IE8では画像がキャッシュされている際に後から設定したonloadが呼ばれないという話が
// あるようなので、逆にそれ以外では後から設定しても呼ばれるのが通例のような気はしますが。。

解決

JavaScript 実行環境のシングルスレッド性の話になります。

Web ページ向け JavaScript の記述で楽なところは、 JavaScript の実行環境が基本シングルスレッドで動くことです。これにより、あるメソッド内のコードが実行中に、別の部分のコードが別のスレッドによって同時に実行されるということがありません。

シングルスレッドによる動作は、イベント処理についても同じように適用され、そこに割り込み処理という概念はありません。画像の読み込みが実質並列処理で、読み込みが完了したからと言って、「ブラウザの対応する処理が完了したその時に、実行中の JavaScript コードを割り込んで別の JavaScript コードが実行されることはない」です。

確かに、画像はブラウザによってマルチスレッドで読み込まれ、JavaScript のあるコードが実行中に、画像の読み込みが完了しているでしょうが、「画像読み込み完了によって発生する 'load' イベント」には、現在実行中のコードが属するイベントハンドラ内のコードの実行が終了してから手がつけられます。

例えば、ページ内でイベントが次々と発生している中、 JavaScript コード(この場合 mouseclick イベントハンドラ)の実行が終わらない場合は、以下の簡略図ように、その間に生成されたイベントが Pending で待ち状態になっていくことでしょう。

イベントハンドラスタック(想像)

質問の complete プロパティの話に戻ります。 <img>complete プロパティが true になるタイミングが <img> で指定された画像が完了し 'load', 'error' イベントが発火する直前で、その変更がネイティブコードによってされるとすれば(ここはドキュメントで明確になっていない)、 JavaScript のコードから見て、 現在 completetrue でさらに onload が将来呼ばれるということはないといえます。

参考:

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *