Aslında back-end 🔩 tarafında kod yazdığım için, javascript dilinde, temelleri 🎯 ve çok bilinen frameworkleri dışında şahsen öyle aman aman bir bilgim ve kullanımım yok. Gerçi yıllar önce Chrome V8 engine bu durumu tepetaklak etti. Tarayıcıya ihtiyaç duymadan sunucu tarafına da müdahale ettiği için bana göre biraz 'hybrid' bir dil haline geldi. Yanı sıra implements, interface, instanceof, private, public gibi aşina olunan keywordler revize edilmiş durumda.

Yani js uzun süre daha yerini koruyacağa benziyor. Bu yüzden bugün çok işe yarar püf noktalarının birinden bahsetmek istiyorum. 'External' kullanımlar için yani <script src="script.js" /> ya da <script src="//google.com/script.js" /> gibi domain dışında bir kullanımda geçilebilecek iki argümandan bahsedeceğim: async ve defer. Aslında bu iki sihirli kelime javascriptin çalışma şeklini kısmen değiştiriyor. Bu yüzden javascript nasıl çalışır, statik olmayan bir sayfa 'nasıl ayağa kalkar', DOM manipulation nedir gibi konulara değinmeyeceğim. Gereksiz yere akıl karıştırmak ve yazıyı şişirmek istemiyorum. Ama bunları araştırmanızı tavsiye ederim

Bu argümanları inline kullanırsanız derleyici herhangi bir hata vermez ancak HTML parse veya scriptler için hiç bir etkisi olmaz.

Önce bu iki argüman kullanmadan sayfanın nasıl ilerlediğine bakalım.

<script>

Aşağıdaki grafikte göreceğiniz gibi script etiketi ile karşılaşılana kadar HTML parse edilmeye devam edilir; ancak etiketle karşılaşılınca parse işlemi bloklanarak script indirilerek çalıştırılmaya başlanır.

legend

legend

Günümüzün modern web uygulamalarında script dosyaları HTML satırlarından daha yüklü satırlar içerdiği için boyutları çok daha fazladır. Bu yüzden eskiye nazaran, kendi alan adınız ya da 3.parti bir script dosyasının indirilerek işlenmesi daha uzun sürer. Normalde tarayıcı HTML elementlerini yüklerken <script src="... /> gördüğü satırda dosyanın indirilmesini bekler, ancak script indirilip çalıştırıldıktan sonra sayfanın geri kalanını parse etmeye devam edebilir. Yani script etiketinden sonraki tüm satırlar bloklanır. Bu, temelde iki önemli soruna yol açar.

  • Sayfada script dosyasından sonraki DOM elementleri ile ilgili bir işlem yapılamaz.
  • Eğer script boyutu çok büyükse sayfa yüklenene kadar HTML bloklanacaktır.
<!doctype html>
<body>
<p id="first-p">Bu satır script tagından önce</p>

<script src="script.js" />

<!-- Bu satır script dosyası yüklenene kadar gözükmez -->
<p id="second-p">Bu satır script tagından sonra</p>
</body>

Bu sorun window.onload event'e function geçilerek ya da script sayfanın en altına eklenerek aşılır.  Sadece script dosyaları değil artık HTML de oldukça yüklü boyutlardadır ve bu durumda da ilgili fonksiyon çıktısı için önce HTML'in tüm işinin bitirmesini DOMContentLoaded event bekleyecektir. Yani bu, sorunu çözmekten uzaktır. Nispeten hızlı bağlantılarda çok sorun olmaz ama yavaş hızlarda bu olay çok fazla göze batar hale gelir.

<script defer>

legend

legend

Bu attribute tarayıcıya script etiketini görse de hiç dokunmadan HTML okumaya devam etmesini bildirir. Aynı sırada script dosyası arka planda indirilir ve HTML tamamen oluşurulduğunda çalışır. Yukarıdaki örneği defer ile deneyelim.

<!doctype html>
<body>
<p id="first-p">Bu satır script tagından önce</p>

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

<!-- Bu satır script dosyasını beklemeden görünür hale gelecektir-->
<p id="second-p">Bu satır script tagından sonra</p>
</body>
  • Sayfa bloklanmadan HTML işlenmeye devam eder.
  • Script dosyası, DOM hazır olduğunda, DOMContentLoaded event öncesinde çalıştırılır.
<!doctype html>
<body>
<p id="first-p">Bu satır script tagından önce</p>

<script>
     document.addEventListener('DOMContentLoaded', () => console.log("defer sonrası hazır DOM"));
</script>

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

<!-- Bu satır script dosyasını beklemeden görünür hale gelecektir-->
<p id="second-p">Bu satır script tagından sonra</p>
</body>

Yine bu örnekte de sayfa bloklanmadan görünür. DOMContentLoaded event handler defer attribute ile işaretlenmiş olan script dosyasını bekler. Bu dosya indirilip çalıştırıldıkdan sonra tetiklenecektir.

Bu attribute kullanan farklı external dosyalar normal sıralarını koruyarak işleme alınır. Modern tarayıcılar, performans için tüm sayfayı tarayarak script etiketlerini aynı anda indirmeye başlarlar ve script dosyaları birbirini bloklamadan işlemlerine devam edecektir. Ancak defer kullanımı; tarayıcıya dosyanın HTML'i bloklamamasını bildirmenin dışında, dosyaların normal sırasını da koruması gerektiğini bildirir. 

<script defer src="long.js" />
<script defer src="small.js" />

Yani yukarıdaki örnekte önce long.js, daha sonra small.js indirilecektir. Eğer defer kullanmasaydık small.js alt satırda olmasına rağmen boyutu daha küçük olduğu için daha erken indirilecek ve eğer long.js içinde kullanacağı bir component varsa hata verecektir.

<script async>

legend

legend
  • defer ile benzer şekilde bu attribute kullananan script etiketleri HTML'i bloklamaz. 
  • Diğer script dosyaları async ile işaretlenen scriptleri beklemediği gibi async ile işaretlenen scrpitler de diğerlerini beklemez.
  • DOMContentLoaded event ve async scriptler de birbirlerini beklemez. Event async dosyasından önce de sonra da tetiklenebilir.  Yani async script boyutu ile ters orantılı olarak çalışır.
  • Sayfa içeriği async ile HTML bloklanmadan görünür kalacaktır. 
  • defer ile benzer şekilde DomContentLoad event öncesi veya sonrası işleme alınabilir.  Yine aynı şekilde büyük boyutlu dosya eğer tarayıcıda önbelleklenmiş ise; örneğin ilk çalıştırmada small.js ikinci çalıştırmada long.js önce çalışır. Haliyle hangisinin önce olacağı ile ilgili kesin bir garantisi yoktur. Eee nolmuş yani ne farkeder ki😄 Fark şu; 1 saniye arayla yaptığınız 2 test tamamen farklı da çıkabilir, aynı da çıkabilir. Bu aslında javascript özelinde değildir. Asenkron programlamayla az-çok uğraştığınızda, çalışma sürelerine göre iş parçacıklarının nasıl birbirine girdiği ve insanı delirtebilecek hale gelmesiyle ilgili bence.😵

Yani async attribute kullanıldığında HTML bloklanmaz fakat defer gibi HTML oluşmasını beklemeden aynı anda asenkron şekilde script dosyasını da indirecektir.

<!doctype html>
<body>
<p id="first-p">Bu satır script etiketinden önce</p>

<script>
     document.addEventListener('DOMContentLoaded', () => console.log("defer sonrası hazır DOM"));
</script>

<script async src="long.js" />
<script async src="small.js" />

<!-- Bu satır script dosyasını beklemeden görünür hale gelecektir-->
<p id="second-p">Bu satır script etiketinden sonra</p>
</body>

Önceki örnekte defer kullandığımız için dosya sırası korunurken, burada dosyalar asenkron şekilde indirilip çalıştırılmaya başlayacak ve small.js boyutu küçük olduğu için 'muhtemelen' daha önce çalışacaktır.

Özellikle 3.parti scriptlerin çoğunun dökümanında bu async attribute zaten yer alır. Mesela google analytics, sayfanın görünürlüğü ile bir ilgisi olmadığı için asenkron şekilde HTML ile birlikte arkada inecek ve performansı gereksiz yere yormayacaktır. 

Sonuç olarak; her iki attribute için de asıl amaç script indirilirken sayfayı bloklamamaktır. 

#SıralamaDOMContentLoaded
asyncscript sırası önemli olmadan ilk indirilen önce çalışırHTML'in ya da scriptin boyutuna göre çalışma sırası ile ilgili bir kesin bir garantisi yoktur. Boyutu küçük olan önce çalışır.
deferscript sırası bildirilen sıraya göre indirilerek çalıştırılırHTML tamamen yüklenip parse edildikten sonra bu event'in hemen öncesinde çalışır.

Eee madem bu kadar avantajı var o zaman tüm scriptlere async ya da defer verelim gitsin... mi?🤔 Bu tamamen projedeki önceliklerinize bağlı. Yani bu iki attribute kullanıldığı zaman scriptler indirip çalıştırılmadan sayfa kullanıcıya gösterilir; scriptler ile sayfada yapacağınız  ayarlamalar henüz sayfada kullanılabilir durumda değildir. Bu durumda script indirilip çalıştırılana kadar sayfada bozulmalar meydana gelecek, daha sonra toparlayacaktır. Arama motorları, bir siteye geldiğinde bizim gözle gördüğümüz gibi yorumlamayacaktır. Yani daha sade bir dille; arama motoru dediğimiz saliselik 'ziyaretçi' sadece koddan anlar ve aslında bizim gözümüze hoş gelen her animasyon onu koda odaklanmaktan alıkoyacağı için bundan pek hoşlanmaz. O yüzden en hızlı ve SEO dostu siteler kupkuru HTML çıktıları olan sitelerdir. Mesela ben, her ne kadar kupkuru bir HTML en iyisidir desem de bu sitede bir sürü animasyona izin verdim ancak temel script dosyam için bile bu argümanları kullandım çünkü performans tarafını öne çekmek istedim. Bu yüzden bazen sayfanın ilk açılışında  toparlamasını gözle görebilirsiniz. Bazen gördüğünüz sitenin yüklendiğini bildiriren ortadaki loading animasyonları aslında bunu gizlemek içindir. Bu yüzden şu yol doğrudur diye bir şey olmaz, ben sadece kullanımlarından bahsettim. Bunun kararını hız&erişilebilirlik&görsellik gibi ihtiyaçların kesişimine göre, proje özelinde kendiniz vermelisiniz.👍


<script>: The Script element-MDN

async and defer - Groving with the web