.js ファイルを非同期で読み込む

一応以下のようにすれば非同期読み込みになります。

<script async src="..."></script>

ですが、これだと100%読み込む事になってしまうので、読み込みたいタイミングで読み込む方法です。

script を動的に作成する

流れは、

  1. <script>タグを作る
  2. 属性を設定
  3. <body>あたりに埋め込む

です。

script タグを作る

document.createElementを使います。scriptを作りたいなら、

const script = document.createElement('script');

です。これで DOM 上にはまだありませんが、<script>タグが作れました。

属性を設定

以下でasyncsrc辺りを設定します。

script.async = 1;
script.src = '...';

body あたりに埋め込む

<script>は DOM に埋め込んでから初めてソースを読み込んでくれるので DOM に追加してあげます。
一番てっとり早いのが<body>タグに埋め込む方法だと思います。これは<body>タグがあればdocument.bodyで取得できます。もし、ない場合は何かの要素にidを振りdocument.getElementByIdなどを使うといいです。

ある要素の下に要素を追加するにはNode#appendChildを使います。以下は追加するコードです。

document.body.appendChild(script);

これでこのような HTML になり読み込みしてくれるはずです。

<body>
  <script async src="..."></script>
</body>

読み込みが終わったタイミングを知る

非同期で読み込むソースが何か別ソースの依存だったり、処理の関係上「Aの読み込みが確実に終わった後にBの処理をしたい」ということは結構あると思います。これには<script>loadイベントを使います。

loadイベントは<script>はソースが読み込み終わった時に発火されます。そのハンドラー関数に続きの処理を書いた関数を設定すれば順番に処理を実行できますね。これは、DOM に追加する前に設定する必要があります。

script.addEventListener('load', () => {
  alert('読み込み完了');
};

document.body.appendChild(script);

script 埋め込みを非同期関数化する

そのソースの読み込み自体を非同期関数にすると何かと便利に扱えます。これはnew Promiseを返しloadのハンドラー関数にそのresolveを指定するだけです。

const loadScriptXxx = () => {
  return new Promise(resolve => {
    /** 上記の内容 */
    script.addEventListener('load', resolve);
    /** 上記の内容 */
  });
};

これでasync-awaitで分かりやすく書けるかもです。

(async () => {
  await loadScriptXxx();
  await loadScriptYyy(); // 例えば Xxx 依存
  await loadScriptZzz(); // 例えば Yyy 依存

  alert('Xxx, Yyy, Zzz 読み込み完了!')
})();