Script eksternal di <head> memberi kerapian, tetapi membawa biaya yang sudah kita lihat: parser berhenti setiap kali menemui tag <script src>, menunggu unduhan, mengeksekusi, lalu melanjutkan kerja. Semakin lambat jaringannya, semakin lama pengguna menatap halaman yang belum selesai. Atribut defer dan async mengubah perilaku itu: keduanya membiarkan parser terus bekerja selagi berkas diunduh, dengan aturan eksekusi yang berbeda.
Kedua atribut hanya berlaku untuk script eksternal — script yang memiliki atribut src. Setelah chapter ini, dilema “di <head> atau di akhir <body>” selesai: posisi bisa rapi di <head>, sementara perilakunya diatur lewat atribut.
Peta Tiga Mode Pemuatan #
Tabel berikut membandingkan script klasik (tanpa atribut) dengan dua modifikasi yang dibahas chapter ini:
| Mode | Unduhan | Waktu eksekusi | Urutan antar-script | Aman membaca elemen |
|---|---|---|---|---|
| Tanpa atribut | Menunggu parser (blocking) | Langsung setelah unduhan selesai | Sesuai urutan tag | Tidak untuk elemen di bawah tag |
defer | Paralel dengan parsing | Setelah seluruh dokumen selesai di-parse | Sesuai urutan tag | Aman |
async | Paralel dengan parsing | Segera setelah unduhan berkas itu selesai | Tidak terjamin | Tidak aman |
Kuncinya ada di dua kolom terakhir. defer menunda eksekusi sampai dokumen siap, sehingga urutan dan akses elemen tetap terjaga. async menyerahkan waktunya pada kecepatan unduhan, sehingga urutan bisa berubah-ubah. Keduanya menghilangkan masa tunggu parser; yang membedakan adalah konsekuensi yang kita terima.
Kedua atribut ini lahir dari satu masalah yang sama: unduhan berkas lewat jaringan jauh lebih lambat daripada parsing. Membuat parser diam sambil menunggu unduhan adalah pemborosan, karena mengunduh tidak perlu antre dengan membaca dokumen. Browser modern bahkan melakukan pramuat (preload scan): saat parser masih membaca bagian atas dokumen, browser sudah memindai tag src di bawahnya dan mulai mengunduhnya lebih awal. Atribut defer dan async kemudian menentukan kapan hasil unduhan itu boleh dieksekusi.
defer: Eksekusi Setelah Dokumen Siap #
Kita buktikan janji tabel itu dengan dua berkas script. Yang pertama membaca elemen halaman:
const status = document.getElementById("status");
console.log("Pemasangan:", status.textContent);Yang kedua hanya memastikan urutannya:
console.log("Verifikasi: kedua script defer berjalan berurutan");Keduanya kita taruh di <head> — posisi yang pada chapter Script Tag justru memicu TypeError — tetapi kali ini dengan atribut defer:
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Dasbor Toko</title>
<script src="skrip-pemasangan.js" defer></script>
<script src="skrip-verifikasi.js" defer></script>
</head>
<body>
<h1>Dasbor Toko</h1>
<p id="status">Semua sistem siap</p>
</body>
</html>Output yang Dihasilkan (pada tab Console browser):
Pemasangan: Semua sistem siap
Verifikasi: kedua script defer berjalan berurutanDua hal yang mustahil pada chapter sebelumnya kini terjadi. Pertama, script berada di <head> tetapi berhasil membaca <p id="status"> — eksekusinya ditunda sampai seluruh dokumen selesai di-parse. Kedua, urutan tetap: skrip-pemasangan.js selalu berjalan sebelum skrip-verifikasi.js, apa pun ukuran atau kecepatan unduhannya, karena defer mengeksekusi sesuai urutan tag.
Mari kita telaah baris demi baris:
- Baris 6 (
<script src="skrip-pemasangan.js" defer></script>): unduhan dimulai segera, paralel dengan parsing; eksekusinya diantrekan, bukan dijalankan sekarang. - Baris 7 (
<script src="skrip-verifikasi.js" defer></script>): proses yang sama; kedua berkas diunduh bersamaan, dan keduanya masuk antrean eksekusi sesuai urutan tag. - Baris 11 (
<p id="status">Semua sistem siap</p>): elemen target. Saat script akhirnya dieksekusi — setelah parser selesai dengan seluruh dokumen — elemen ini sudah berada di DOM, sehinggagetElementByIdberhasil.
Urutan lengkapnya di balik layar: parser menemui kedua tag defer dan melanjutkan membaca dokumen; unduhan berjalan paralel; parsing selesai; kedua script dieksekusi berurutan; halaman siap dipakai. Tidak ada satu detik pun parser berhenti menunggu.
Ada satu penanda lain yang layak dicatat: eksekusi script defer selesai sebelum event DOMContentLoaded terpicu — event yang menandai seluruh dokumen telah siap. Artinya, kode lain yang menunggu event itu dapat mengasumsikan script defer sudah tuntas menjalankan tugasnya. Kita akan bertemu event tersebut lagi ketika course membahas browser events.
async: Eksekusi Segera Setelah Unduhan #
async memakai strategi yang berbeda: unduhan paralel, lalu eksekusi segera begitu berkas itu selesai diunduh — tanpa menunggu parsing selesai dan tanpa menunggu script lain. Contoh dua berkas yang saling lepas:
console.log("Statistik pengunjung dikirim");console.log("Banner promosi dimuat");<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Dasbor Toko</title>
<script src="skrip-statistik.js" async></script>
<script src="skrip-banner.js" async></script>
</head>
<body>
<h1>Dasbor Toko</h1>
</body>
</html>Output yang Dihasilkan (pada tab Console browser) — salah satu dari dua kemungkinan:
Statistik pengunjung dikirim
Banner promosi dimuatPemuatan ulang berikutnya bisa saja menghasilkan urutan terbalik. Itu bukan galat — begitulah async dirancang. Urutan eksekusi mengikuti urutan unduhan selesai, dan unduhan bergantung pada ukuran berkas serta kondisi jaringan. Jika skrip-banner.js ternyata lebih kecil dan lebih dulu tiba, skrip-banner.js lebih dulu dieksekusi.
Mari kita telaah halamannya baris demi baris:
- Baris 6 (
<script src="skrip-statistik.js" async></script>): unduhan dimulai segera, paralel dengan parsing; begitu selesai, isi berkas dieksekusi tanpa menunggu apa pun. - Baris 7 (
<script src="skrip-banner.js" async></script>): unduhan kedua berjalan bersamaan; keduanya saling berlomba, dan yang selesai lebih dulu menang eksekusi. - Baris 10 (
<h1>Dasbor Toko</h1>): parsing bisa selesai sebelum salah satu script dieksekusi — inilah perbedaan mendasarnya dengandefer, yang selalu menunggu parsing tuntas.
Sifat tak terduga ini menuntut independensi yang ketat. Script async tidak boleh bergantung pada script lain: jika skrip-banner.js membutuhkan data yang disiapkan skrip-statistik.js, urutan acak membuat program kadang jalan dan kadang menampilkan galat tergantung hari — jenis bug yang paling sulit dilacak. Bila ada ketergantungan, gunakan defer dan biarkan urutan tag mengatur giliran.
Dengan syarat itulah, async hanya tepat untuk script yang berdiri sendiri: penghitung statistik, widget pihak ketiga, atau script pemuat iklan — kode yang tidak membaca elemen halaman dan tidak bergantung pada script lain.
Melihat Buktinya di DevTools #
Klaim tentang waktu unduhan ini bukan teori belaka; kita bisa memeriksanya langsung dengan DevTools dari chapter Browser DevTools. Buka tab Network di halaman-defer.html, muat ulang halaman, dan perhatikan dua berkas script tercantum beserta durasi unduhannya. Pada koneksi yang disimulasikan lambat (pilihan throttling di tab Network), garis waktu menunjukkan unduhan berjalan bersamaan dengan pembacaan dokumen — bukan setelahnya. Tab Sources juga menampilkan kedua berkas sebagai sumber terpisah, lengkap dengan nomor baris untuk memasang breakpoint bila kita ingin memastikan kapan eksekusinya berjalan.
Memilih Atribut yang Tepat #
Keputusannya bisa diringkas menjadi daftar pemeriksaan singkat:
- Script membaca elemen DOM, atau bergantung pada script lain? →
defer. - Script sepenuhnya lepas — tidak menyentuh DOM dan tidak peduli urutan? →
async. - Script inline (tanpa
src)? → tanpa atribut;deferdanasynctidak berpengaruh. - Script di akhir
<body>tanpa atribut? → tetap sah; itu pola chapter Script Tag.
Rangkuman chapter ini: defer untuk kode yang butuh halaman siap dan urutan terjaga; async untuk kode lepas yang berdiri sendiri. Lingkungan browser kini lengkap — bab penutup section ini membawa kita keluar dari browser menuju terminal.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
skrip-banner.js
Unduh berkasconsole.log("Banner promosi dimuat");
skrip-pemasangan.js
Unduh berkasconst status = document.getElementById("status");
console.log("Pemasangan:", status.textContent);
skrip-statistik.js
Unduh berkasconsole.log("Statistik pengunjung dikirim");
skrip-verifikasi.js
Unduh berkasconsole.log("Verifikasi: kedua script defer berjalan berurutan");