28821


**複数のファイルに依存関係がある場合には、async 属性と defer 属性の併用は不可でしょうか？**

・「Google Maps API」で両方指定しているのは、htmlにjsを直接記述しているから？

* * *

**質問**

・例えば、[リンク先](https://developers.google.com/maps/documentation/javascript/tutorial?hl=ja)でhtmlに直接記述しているjs部分を外部js(init.js)として読み込む場合にはどう書けば良いでしょうか？

**案1.**

[code]

    <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"></script>
    <script async defer src="init.js"></script>
    
[/code]

**案2.後のみasyncなし**

[code]

    <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"></script>
    <script defer src="init.js"></script>
    
[/code]

**案3.asyncなし**

[code]

    <script defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"></script>
    <script defer src="init.js"></script>
    
[/code]

* * *

**＜追記＞**

**「async 属性」を複数付与した場合は、いつ実行されるかは全く予想がつかないけれども、実行順序は保証される、ということでしょうか？**

[code]

    <script async id="lib" src="https://maps.googleapis.com/maps/api/js"></script>
    <script async src="local.js"></script>
    
[/code]

・回答いただいた内容は、asyncに対応しているブラウザの場合、最終的に上記と同じ挙動になる、ということでしょうか？  
・非同期で最初のスクリプトが処理された後、次のスクリプトが非同期で処理される？

> asyncとdefer両方指定した場合、デフォルトはasyncで、asyncに対応していないブラウザのみdeferのタイミングになります


CC BY-SA 3.0
2016-09-11T02:32:50.260
28813
2016-09-12T05:04:18.977
2016-09-12T04:16:42.887
7886
7886
question
javascript
複数のファイルに依存関係がある場合には、async 属性と defer 属性の併用は不可？


どの案にも対応可能なスクリプトは可能です。一応ですが`async`と`defer`両方指定した場合、デフォルトは`async`で、`async`に対応していないブラウザのみ`defer`のタイミングになります（[ソース](https://www.w3.org/TR/html5/scripting-1.html#attr-
script-async)）。両方指定しているのは一方にしか対応していないブラウザがあるためだと思います。

Google Mapのスクリプトが実行されると`window.google.map`が定義されるので、それの有無を確認（`typeof hoge ===
"undefined"`で）して、あれば即`initMap()`を実行、なければスクリプトに`load`イベントリスナーを付け読み込みが終わった段階で`initMap()`が実行されるようにすればいいのでは無いでしょうか。  
問題はIE8以前で、場合によって`load`イベントは発行されないので、代わりに`readystatechange`イベントで実行が終わったか確認しています。IE8以前なんてサポートしたく無い場合は消していただいて大丈夫です。

具体的には  
html:

[code]

    <html>
    <head>
        <meta charset="utf-8">
        <script defer async id="lib" src="https://maps.googleapis.com/maps/api/js"></script>
        <script defer async src="local.js"></script>
    </head>
    <body>
        <div id="map"></div>
    </body>
    </html>
    
[/code]

local.js:

[code]

    function initMap(a) {
        // var map = new google.maps.Map(document.getElementById('map'), {
        //     center: {lat: -34.397, lng: 150.644},
        //     zoom: 8
        // });
        console.log("init ok", a);
        console.log(google.maps);
    }
    
    if(typeof google !== "undefined" && typeof google.maps !== "undefined") {
        initMap("fast");
    } else {
        var lib = document.getElementById("lib");
        if(typeof lib.onreadystatechange === "undefined") {
            //IE9以降 + その他ブラウザ用
            lib.onload = function() {
                initMap("slow");
            };
        } else {
            //IE8以前用　（チェックしてません）
            //リスナーをつける場合は addEventListener ではなく attachEvent （これもIE8以前特有な変わった点）
            lib.onreadystatechange = function() {
                if (lib.readyState === "complete") {
                    initMap("slow");
                }
            };
        }
    }
    
[/code]

`"fast"`とか`"slow"`は順序確認のために足しただけです。Google
Mapのみ`defer`した場合は"slow"、local.jsのみ`defer`した場合は"fast"となるはずです。

* * *

## 追記

Google Mapのサンプルの仕組みについてお気付きで無い点があるかもしれないので、思ったことを少し。  
まず`initMap()`が呼び出されるのはGoogle
MapライブラリのURLに`&callback=initMap`とコールバック関数を指定しているからです。これはライブラリの機能ですが、ライブラリの読み込みが終わった段階で実行したい関数を指定できる様になっています。`initMap()`がライブラリから実行されるため、`initMap()`はライブラリの初期化処理が開始される前に定義されていないといけません。そのためサンプルでは`initMap()`はインラインでライブラリスクリプトの前に書かれています。この構造のまま`initMap()`を外部スクリプトにしたい場合は、そのスクリプトをライブラリの前に置いて、なおかつ`async`も`defer`も指定しなければ大丈夫です。  
ただ、質問感じから`initMap()`を含むスクリプトも`async`+`defer`したい様でしたので上の解答を書きました（ライブラリの`&callback=initMap`機能に頼らず、自前でライブラリの初期化状況を確認）。


CC BY-SA 3.0
2016-09-11T10:55:43.243
28821
2016-09-12T05:04:18.977
2016-09-12T05:04:18.977
3974
3974
28813
answer