◆ 動的に追加したasync=false の script タグと HTML に直接書かれているタグでは扱いが違う
◆ パースされて head タグに加えられていなくても すでにリクエストされているので DOM の順番と実行される順番が違うことがある
◆ 動的に追加する script タグの後に実行したいスクリプトなら それも動的にロードして async=false で順番に実行するのがよさそう

async を false にすると順番に読み込んでくれるのですが JavaScript で動的に追加したものしかだめなようです

1

[main.html]
<!doctype html>
<meta charset="utf-8">
<script>
var val = 100
function loadFile(url){
var s = document.createElement("script")
s.async = false
s.src = url
document.head.appendChild(s)
}
loadFile("test.js")
</script>
<script>
console.log(val)
console.log(document.head.innerHTML)
</script>

loadFile は指定 url のファイルを async = false で document.head に append する関数です

[test.js]
val = 200
console.log("loaded test")

1 つめの script タグで loadFile("test.js") をしているので HTML での 2 つめの script タグより先に test.js が DOM上は設置されてるはずです
なので 先に val が 200 にされてから console.log の表示にうつるように思えます

ですが
100
<meta charset="utf-8">
<script>
var val = 100
function loadFile(url){
var s = document.createElement("script")
s.async = false
s.src = url
document.head.appendChild(s)
}
loadFile("test.js")
</script><script src="test.js"></script>
<script>
console.log(val)
console.log(document.head.innerHTML)
</script>

loaded test

途中にある HTML は document.head の innerHTML で test.js のあとに console.log があります
なのに loadFile で読み込んだファイルが後から実行されています

2

HTML での 2 番目の script がソースに直接書いてるから defer 無効化みたいな感じで特別扱いされてるのかなと test2.js に置き換えてみます

[main.html]
<!doctype html>
<meta charset="utf-8">
<script>
var val = 100
function loadFile(url){
var s = document.createElement("script")
s.async = false
s.src = url
document.head.appendChild(s)
}
loadFile("test.js")
</script>
<script src="test2.js"></script>

[test2.js]
console.log(val)
console.log(document.head.innerHTML)
100
<meta charset="utf-8">
<script>
var val = 100
function loadFile(url){
var s = document.createElement("script")
s.async = false
s.src = url
document.head.appendChild(s)
}
loadFile("test.js")
</script><script src="test.js"></script>
<script src="test2.js"></script>

loaded test

変わらず loadFile 関数を使ったものが後から実行されます

3

async=false の動作が変わったとかないよね とtest.js と test2.js の両方とも loadFile にして確認してみます

[main.html]
<!doctype html>
<meta charset="utf-8">
<script>
var val = 100
function loadFile(url){
var s = document.createElement("script")
s.async = false
s.src = url
document.head.appendChild(s)
}
loadFile("test.js")
loadFile("test2.js")
</script>
loaded test

200
<meta charset="utf-8">
<script>
var val = 100
function loadFile(url){
var s = document.createElement("script")
s.async = false
s.src = url
document.head.appendChild(s)
}
loadFile("test.js")
loadFile("test2.js")
</script><script src="test.js"></script><script src="test2.js"></script>

ちゃんと test.js → test2.js の流れで実行されています


DOM 構築時の順番じゃなくて HTML に直接書いたものが優先されるみたい

4

delay をさせてみます

[main.html]
<!doctype html>
<meta charset="utf-8">
<script>
var val = 100
function loadFile(url){
var s = document.createElement("script")
s.async = false
s.src = url
document.head.appendChild(s)
}
loadFile("test.js")
</script>
<script>for(var i=0;i<100000000;i++) i</script>
<script>for(var i=0;i<100000000;i++) i</script>
<script>for(var i=0;i<100000000;i++) i</script>
<script>for(var i=0;i<100000000;i++) i</script>
<script>for(var i=0;i<100000000;i++) i</script>
<script src="test2.js"></script>

test2.js を読み込む前に script タグを 5 つ用意してだいたい 1~2 秒かかります
この時間の間で test.js は読みこまれていて test2.js はまだ読みこまれていないのでいけるかな と思ったけど
100

loaded test

だめでした

5

DOM を大量に挟んでみます

[main.html]
<!doctype html>
<meta charset="utf-8">
<script>
var val = 100
function loadFile(url){
var s = document.createElement("script")
s.async = false
s.src = url
document.head.appendChild(s)
}
loadFile("test.js")
</script>
<div></div>
<!-- ↑の div を 一万個くらい -->
<script src="test2.js"></script>

すると
loaded test

200

なんと test.js を先に読みこんでくれています

原因がよくわからない・・・

6

2 のコードで loadFile のところで debugger で一時停止します
すると test2 はまだ DOM では作られていないのにすでに Network の一覧にありました

つまり DOM 構築前にパースは終わっていてリクエストは先に送っているみたいです
それでも DOM が多すぎると一度に全部パースはしないで区切ってるのか test2.js が先にリクエストしないことがあります
その場合は test.js が先にロードされて順番が変わるということだと思います


なんか複雑

とりあえず HTML に最初から書いているのと 動的に追加するものでロード順は決まらないので 動的なロードの後に 実行したいのなら非同期処理を考慮するように作るか 動的処理が絡むところ以降は全部動的にロードして async=false で順番制御がよさそうです

* これはあくまで Chrome での調査結果です