HTMLタグのscriptタグにはtypeとsrc以外にも指定できることがあるんです

deferとasyncどちらもコードの実行タイミングを変更する属性です

指定なし

<script src="test.js"></script>
scriptタグが出てきたタイミングでJavaScriptを実行します

途中でdocument.write()でHTMLを出力できます
最近はそんなことしてるのをほとんど見ませんが

JavaScriptファイルが大きすぎるとJavaScriptのパース&実行中はHTMLの解析が進まないので表示の遅いページができ上がります
bodyの終了タグ直前にscriptタグを書くことが推奨されたりもしますが私は先に(headタグ内)に書きたい派です

bodyの終了タグ直前に書く場合は困りませんが bodyより前にscriptを書く場合はdocument.getElementById()などでDOMを取得するとnullになります
そのまま
document.getElementById("test").onclick = function(){ };
なんてすると nullにプロパティはありませんエラーになってしまいます
bodyをまだ読んでないんだからDOMに何があるのかわからないから当たり前といえば当たり前です
それでもよくやってしまうミスです

対策はDOM処理をini関数内に書いておいて
<body onload="ini()">
とか書いてみたり JavaScript内で
window.onload = ini;
または
window.addEventListener("load", ini, false);
としてDOMができてからアクセスするのが一般的でよく見る方法です

defer

<script src="test.js" defer></script>

この属性をつけるとスクリプトの実行が文章 つまりHTMLファイルを読み込んだ後になります

ということは DOMが構築されたあとなのでdocument.getElementById()などの関数を後から呼ぶ必要がなくなります
ただし 先にJavaScriptを読み込んだ時と違って 表示はできているのにJavaScriptがダウンロード終わっていないという場合もネット回線が良くないと起こりえます
そうなるとJavaScriptで設定されるonclickなどが 画面が表示されてからしばらく反応しないというのもありえますね

この属性の注意するところは src属性があって外部のファイルを読み込むときにだけ効果があるということです
scriptタグ内にJavaScriptコードを書いている場合はdefer属性がないのと同じで先に実行されます
defer属性はDOM操作のコードをhead内のscriptタグでできるようにするというより 別ファイルを読み込むせいで時間かかることを防ぐものなので HTMLのbodyに辿り着くまでに上から順番に読んでいくんだから 読んだコードはそのまま実行する ってことですね
<script defer>
    var h1elem = document.getElementById("test"); // エラー!
</script>
<h1 id="test">H1</h1>

Firefoxの過去版ではscriptタグ内に直接書いたコードでも後で実行してくれた頃があったようです

async

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

deferよりこっちのほうが有名だと思います
Google関連でたまにこの属性がついたscriptタグを見ます

非同期でファイルを読み込んでくれて読み込みが終わったタイミングで実行されるものです
deferのようにDOMの構築後と保証されてません
「ファイルのダウンロードが完了して読み込み可能になったら」です

とは言っても 並列処理でHTML読み込み中にJavaScriptが実行されてることはないと思うので結局実行されるのはHTML読み込みが終わってDOMできてからになるんじゃないかなーと思ってます

HTML解析からDOM構築は内部的にJavaScriptで行われてるはずなので 普段通りに シングルスレッドで HTMLを読込中が終わって次に登録されてる処理(asyncのダウンロード完了したコードのパース&実行)をするという流れだと思います(たぶん)