複数の外部JSファイルを読み込む時の実行順序について

2014年10月14日

カテゴリー:

これまであまり意識してこなかったのですが、JavaScriptコードを外部ファイルとして作成し、それらのファイルをHTMLに読み込む際、外部ファイルに記述されたコードはいつどのようなタイミングで実行されるのか?と、ふと疑問に思いました。

大きなプロジェクトであればあるほど、肥大化したJavaScriptコードを機能毎に別ファイルに切り出し、体系的にJavaScriptコードを管理した方がメンテナンスもし易くなると思いますが、そいうったケースでは複数の外部JSファイルをHTMLファイル内に読み込むことになると思います。

そのような場合、JSファイル間で依存関係が有ったりすると、コードの実行される順序が重要になってくると思うのですが、この順番は保証されているのでしょうか? 例えば、外部JSファイルのファイルサイズや、ネットワークトラフィックの都合で特定のJSファイルのロードに時間がかかってしまうような場合、<script>タグの出現順序に関係なくロードが完了した別のJSファイルから処理されたりするのでしょうか?

もしかしたら、これってものすごく初歩的なことのような気もするのですが、ネットで調べてみても「複数の外部JSファイルの実行順序」という点について解説している記事を見つけられなかったので、今回はそのあたりを検証を交えながら少し掘り下げてまとめてみました。

※ちなみに、(JSONPで使われるような)外部JSファイルの動的ロードは検証の対象外としました。あくまでもHTML上の記述された<script>タグの実行順序についての内容となっています。

検証概要

例えば以下の様なコードがあるとします。

<html>
  <head>
    <script src="first.js"></script>
    <script src="second.js"></script>
  </head>
  <body>
  .....
  </body>
</html>
var firstObject = {};
//firstObjectというグローバル変数が存在する前提のコード
firstObject.secondObject = {};

極端な例ではありますが、上記の場合、first.js が firstObject というグローバル変数を作成し、second.jsでグローバル変数 firstObject に secondObject というプロパティを追加しています。
この例では first.js second.jsの順で実行される必要がありますが、上記のようなHTMLコードではたしてこの順番は保証されるのでしょうか?

例えば、first.js、second.jsがそれぞれ別のサーバにあった場合、ネットワークトラフィックの影響などにより、first.jsロードが遅れ、second.jsの方が先に実行されてしまう可能性ないのでしょうか?

もしくは、ファイルサイズの都合により first.js よりも second.js の方が先にダウンロードが完了してしまった場合、first.jsよりsecond.jsの方が先に実行されてしまう可能性は無いのでしょうか?

仮に、first.js よりも second.js の方が先に実行されてしまった場合、second.js の実行時点では firstObject というグローバル変数は存在していないはずなので、second.jsのコード実行時にはエラーが発生するはずです。

検証1:ネットワークからのレスポンス速度の違うJSファイルの実行タイミング

次の例では、先に実行されるべき first.js を外部サーバに置き、second.jsをローカルのサーバに置いた場合のサンプルです。
当然ながら、外部サーバよりローカルサーバの方が反応が速いので、ローカルサーバ上に配置されているsecond.jsの方が先に読み込みが完了し、そのまま実行されてしまうようにも思えるのですが実際はどうなのでしょう?

<html>
  <head>
    <script src="http://hogehoge.com/first.js"></script>
    <script src="http://localhost/second.js"></script>
  </head>
  <body>
  ...
  </body>
</html>
console.log('first script');
console.log('second script');

上記ページをブラウザで表示させ、コンソールログを確認してみた結果、以下の通り出力されました。(画面はChromeのデベロッパーツール)

chrome_console_141030_01

結果を見ると、first.js、second.jsの順で実行されていることが分かります。そして、下図は各スクリプトのロード状況を表示させたものです。

chrome_console_141030_02

これをみると、やはりsecond.jsの方が先にダウンロードが完了しています。にもかかわらず first.js、second.js の順で実行されているということは、second.jsはfirst.jsのロード・実行が完了するまで「待っている」ということになりますね。

検証2:ファイルサイズの違うJSファイルの実行タイミング

次の例では、first.js、second.js共に同じ外部サーバに配置していますが、first.jsのファイルサイズを極端に大きくしています。(first.jsのファイルサイズ:50MB、second.jsのファイルサイズ:1KB未満)

こちらの例でも先ほどの検証1の場合と同様、second.jsの方が先にダウンロードが完了するように思えるのですが、この場合もちゃんとfirst.js、second.jsの順で実行されるのでしょうか?

<html>
  <head>
    <script src="http://hogehoge.com/first.js"></script>
    <script src="http://hogehoge.com/second.js"></script>
  </head>
  <body></body>
</html>
console.log('first script');
/*
膨大な量の文字列
(コメントアウトされているので、プログラムの実行には影響しない)
*/
console.log('second script');

上記ページをブラウザで表示させ、コンソールログを確認してみた結果が以下です。

chrome_console_141030_03

検証1の結果同様、こちらの場合も first.js、second.js の順で実行されていることが分かります。そして、下図は各スクリプトのロード状況を表示させたものです。

chrome_console_141030_04

second.js は即座にダウンロード完了しているのに対し、first.js はダウンロードが完了するまでに10秒近くかかっています。しかしコンソールログを見ても分かる通り、first.js、second.js の順でコードが実行されています。

検証3:外部JSファイル、インラインスクリプト、スクリプト以外のタグの混在時の実行順序

ここまでの検証結果を踏まえると、複数のJavaScriptファイルをHTMLに取り込む場合、ネットワークのやファイルサイズの影響に関係なく、<script>タグの出現順序通りにJavaScriptコードが実行されていることが分かります。

では、外部JSファイルだけではなく、インラインスクリプトも混在している場合はどういった表示順序になるのでしょうか?

次のコードは、検証2のHTMLコードを少し修正し、外部JSファイルだけでなく、インラインスクリプトとスクリプト以外のタグ(<p>タグ)も入れ込んだものです。

<html>
  <head></head>
  <body>
    <script src="http://hogehoge.com/first.js"></script>
    <p>テストです。</p>
    <script>
    console.log('inline script');
    </script>
    <script src="http://hogehoge.com/second.js"></script>
  </body>
</html>

上記の例では、

  • 外部JSファイル(非常に重いファイル)
  • pタグ
  • インラインスクリプト
  • 外部JSファイル(軽いファイル)

の順で記述しています。このHTMLをブラウザで表示させた場合の結果が以下です。

chrome_console_141030_06

スクリプトは first.js → インラインスクリプト → second.js の順で実行されていることが分かります。また、この図からは分かりませんが、first.jsの実行が完了するまでは<p>タグの中身「テストです。」は表示されませんでした。

つまり、外部JSファイルであれ、インラインスクリプトであれ、スクリプト以外のマークアップであれHTML上での出現順序通りにコードが実行される、ということが言えそうですね。これは言い方を変えると、スクリプトのロード・解析/実行が完了するまで、以降のHTMLマークアップの解析は「一時停止状態」になるということです。

まとめ

ここまでの検証結果を踏まえると、HTMLマークアップの中に複数の<script>タグ(外部ファイル参照やインラインスクリプト)や、スクリプト以外のマークアップが存在する場合でも、HTML上での出現順序通りに順次処理される、と言えるでしょう。(※<script>タグが<head>タグ内にあっても<body>タグ内にあっても結果は同じ)

また、特定のスクリプトのロードに時間がかかってしまった場合、そのスクリプトのロード・実行が完了するまでは、次のスクリプトは実行されないということが分かりました。


【以下の記事を参考にさせて頂きました】
[JavaScript, HTML] script要素が実行される順番:基本は関数と同じ | Ouka Studio
【JavaScript】記述方法別の実行タイミングについて – Qiita
Windows TIPS:巨大なサイズのファイルを簡単に作る方法 – @IT