Etkileşim performansı / ana iş parçacığı

Main Thread Blocking Nasıl Azaltılır?

Main thread blocking sorununu anlatan tarayıcı iş yükü görseli

Bir sayfa açıldıktan sonra kullanıcı düğmeye basıyor, menüyü açıyor ya da sekme değiştiriyor ve ekran kısa bir an donmuş gibi kalıyorsa, çoğu zaman sorun ağdan çok ana iş parçacığının dolu olmasındadır.

Main thread blocking denen durum tam olarak budur: tarayıcı, sıradaki işi bitirmeden yeni girdiye alan açamaz. Kullanıcı komut vermiştir ama o komut kuyruğa takılır. Deneyim burada ağırlaşır.

Bu yüzden problem sadece “fazla JavaScript var” diye özetlenemez. Belirleyici nokta, hangi işin ne zaman çalıştığı ve tarayıcının o anda başka neyle meşgul olduğudur.

Main thread'in neden kritik olduğu

Tarayıcının ana iş parçacığı; JavaScript çalıştırma, stil hesaplama, layout, boyama ve pek çok etkileşim akışının merkezindedir. Önüne gelen görevler burada sıraya girer. Kuyruk şişerse kullanıcı komutu bekler.

Bu yüzden ağır bir görev yalnızca kendi süresi kadar sorun yaratmaz. Arkasından gelen tıklamayı, kaydırmayı ve görünür ekran güncellemesini de geciktirir. Kullanıcının hissettiği “takılma” çoğu zaman bu zincirleme beklemedir.

Özellikle CPU süresi yükseldiğinde ana iş parçacığındaki daralma daha görünür olur. Çünkü pahalı görevler yalnızca çok çalışmakla kalmaz, başka işlerin de önünü kapatır.

Sorunun kullanıcı tarafındaki hissi

Main thread blocking çoğu zaman yüklenme raporunda tek başına bağırmaz; ama kullanıcı davranışında hemen hissedilir. Düğmeye basarsınız, menü geç açılır. Arama alanına yazarsınız, harfler takılarak gelir. Filtre değiştirirsiniz, sonuçlar geç tepki verir.

Bu durum özellikle INP metriğinde belirginleşir. Çünkü etkileşim geldiği anda ana iş parçacığı meşgulse, tarayıcı yeni görsel yanıtı hemen üretemez. Sorun bazen ilk yükte değil, kullanım anında ortaya çıkar.

Kullanıcı tarafında hissedilen gecikme çoğu zaman “ağ yavaş” diye yorumlansa da, gerçek darboğaz bazen tamamen istemci tarafındaki görev yoğunluğudur.

Ana iş parçacığını en çok bloklayan işler

En sık karşılaşılan kaynak ağır JavaScript yürütmesidir. Büyük bundle'lar, senkron iş akışları, tek seferde çalışan büyük hesaplar ve gereksiz yeniden render zincirleri ana iş parçacığını hızla doldurur.

Ama tablo sadece kod çalıştırmaktan ibaret değildir. Stil hesaplama, ölçüm yapıp hemen stil değiştirme, büyük DOM güncellemeleri, pahalı animasyonlar ve üçüncü taraf script'ler de aynı tıkanıklığı üretebilir.

  • Büyük görevler: Tek parça halinde uzun süren işler kullanıcı girdisini bekletir.
  • Yoğun DOM değişimi: Yüzlerce öğeyi aynı anda güncellemek ana thread'i şişirir.
  • Ölçüm ve stil döngüsü: Sık layout okuma-yazma zincirleri maliyeti büyütür.
  • Üçüncü taraf etiketler: Kendi faydası sınırlı olsa da kritik anda yük bindirebilir.

Özellikle JavaScript yük dağılımı yanlış kurulduğunda, kullanıcıya görünmeyen ama deneyimi doğrudan bozan bloklar çok daha kolay oluşur.

Küçük işlerin birikerek büyümesi

Bazı projelerde tek bir dev görev yoktur; ama arka arkaya onlarca orta ölçekli görev çalışır. Bu daha tehlikeli olabilir, çünkü ekip toplam yükü fark etmeyebilir. Her iş “çok da büyük değil” gibi görünür, ama birlikte ana iş parçacığını uzun süre meşgul eder.

Özellikle etkileşim sonrası çalışan zincirlerde bu durum sık yaşanır. Tıklama gelir, state güncellenir, liste yeniden hesaplanır, analytics olayı gönderilir, tooltip konumu hesaplanır, animasyon başlatılır. Tek tek makul görünen bu adımlar birlikte tıkanma yaratır.

Sorunun kaynağını bulmanın yolu, yalnızca en büyük görevi değil, arka arkaya sıkışan iş kümelerini de incelemektir.

Azaltmada ilk bakılacak alan

İlk hedef, kullanıcı etkileşimi geldiğinde çalışan görevleri ayırmaktır. Sayfa yüklenirken çalışan maliyet ile tıklama sonrasında patlayan maliyet aynı değildir. Özellikle ana iş parçacığındaki bloklar etkileşim anında daha kritik hale gelir.

Bu yüzden önce şu soru sorulmalıdır: kullanıcı hangi akışta gecikme hissediyor? Menü, filtre, sekme, arama önerisi, modal açılışı ya da form gönderimi gibi kritik noktalarda çalışan işleri görmek, genel paket analizinden daha hızlı sonuç verir.

Eğer sorun hem yüklenmede hem etkileşimde görünüyorsa, görsel ilerleme ile ana thread iş yükünü birlikte okumak daha doğru olur. Çünkü üst alanın yavaş kurulması ile sonradan gelen tıklama gecikmesi bazen aynı temel maliyetten çıkar.

Büyük görevleri parçalamanın etkisi

Çünkü tarayıcıya nefes alanı açar. Tek seferde çalışan uzun bir iş, kullanıcı girdisini tamamen bloke eder. Aynı iş daha küçük görevlere bölündüğünde tarayıcı aralara tepki verme fırsatı bulur. Bu sayede arayüz donmuş gibi görünmez.

Buradaki amaç işi tamamen yok etmek değil, kritik anda tek parça halinde çalışmasını önlemektir. Özellikle liste sıralama, veri temizleme, büyük JSON işleme veya görsel bileşen hazırlama gibi adımlar küçük parçalara bölündüğünde etkileşim hissi belirgin biçimde toparlanabilir.

Aynı mantık kod küçültmede de geçerlidir. Bazen ağır bir kod yolu, gereksiz boşlukları temizlemek kadar basit olmayan ama yine de hızlı bir ilk adım gerektirir. İlgili parçanın boyutunu ve paket dağılımını görmek ilk taramada işe yarar; bu aşamada dosyaları minify ederek hızlı bir ön kontrol yapılabilir. Yine de asıl kazanç, bloğun ne zaman ve hangi sırada çalıştığını yeniden düzenlemekten gelir.

Beklenen kadar fayda sağlamayan optimizasyonlar

Sadece toplam dosya boyutunu küçültmek her zaman main thread blocking sorununu çözmez. Kod küçük olabilir ama hâlâ yanlış anda ve tek parça halinde çalışıyorsa kullanıcı gecikmesi sürer. Aynı şekilde, süslü geçişler ve görsel hilelerle takılmayı maskelemeye çalışmak bazen yükü daha da büyütür.

Bir diğer yanlış öncelik, yalnızca sunucu tarafını iyileştirip istemci tarafındaki pahalı akışları olduğu gibi bırakmaktır. Sunucu çok hızlı yanıt veriyor olabilir ama tarayıcı o yanıtla ne yapacağını geç tamamlıyorsa sorun bitmez.

Ana iş parçacığından uzaklaştırılması mantıklı işler

Her iş main thread üzerinde kalmak zorunda değildir. Özellikle kullanıcı tıklamasının anlık görsel yanıtını gerektirmeyen hesaplamalar arka plana taşınabiliyorsa, ana iş parçacığı ciddi biçimde rahatlar. Büyük veri dönüştürme, sıralama, filtreleme ya da formatlama işlerinin bir kısmı bu yüzden ayrı yürütme alanlarına aktarılabilir.

Buradaki kritik ayrım şudur: görsel tepki gerektiren küçük işi önde bırakıp, pahalı hesaplamayı kullanıcıya görünür yanıt verdikten sonra ya da arka iş akışında sürdürmek. Bir tıklama sonrası panel önce açılır, ağır liste hesabı birkaç adım sonra tamamlanırsa kullanıcı “komutum alındı” hissini kaybetmez.

Bu yaklaşım her zaman teknik olarak worker kullanmak anlamına gelmez. Bazen sadece pahalı fonksiyonu tetikleme zamanını değiştirmek, bazen de hesaplamayı daha küçük bloklara ayırmak aynı rahatlamayı sağlar. Yani amaç teknoloji seçmekten önce ana iş parçacığına kritik anda alan kazandırmaktır.

Framework tarafında sorunun büyüdüğü yerler

Modern arayüzlerde main thread blocking çoğu zaman framework kullanım biçiminden büyür. Gereksiz state güncellemeleri, çok geniş yeniden render alanları, tek olayda çok sayıda bileşenin tetiklenmesi ve büyük liste ağaçlarının sürekli yeniden kurulması bu sorunu görünmeden besler.

Özellikle filtre paneli, canlı arama, sonsuz kaydırma ve dashboard ekranları bu açıdan risklidir. Kullanıcı tek bir işlem yapar ama arka planda onlarca küçük bileşen yeniden hesaplanır. Sorun kodun yanlış olması değil, güncelleme kapsamının gereğinden geniş olmasıdır.

Bu yüzden sorun ararken yalnızca bundle büyüklüğüne bakmak eksik kalır. Bazen asıl problem, gelen koddan çok o kodun kullanıcı etkileşiminde ne kadar geniş alanı harekete geçirdiğidir. Kimi projede küçük ama kötü yerleştirilmiş bir state akışı, büyük bir dosyadan daha yıkıcı sonuç verir.

Ölçüm sırasında yapılan yanlış okumalar

En yaygın hata, tek bir kayıtta görülen uzun görevi bütün sorunun özeti sanmaktır. Oysa bazı bloklar tekrarlı değil, bazıları ise küçük küçük ama sürekli yaşanır. Kullanıcıyı daha çok rahatsız eden ikinci durum olabilir.

Bir başka hata da yalnızca açılış kaydına bakmaktır. Oysa main thread blocking çoğu zaman kullanım anında ortaya çıkar. Sayfa ilk yükte sakin görünür ama menü açılınca, filtre değişince ya da arama kutusuna yazınca ana iş parçacığı dolmaya başlar. Bu yüzden etkileşim tabanlı kayıt almak şarttır.

Son olarak, tek cihaz profiline güvenmek de risklidir. Masaüstünde kısa görünen bir blok, orta sınıf mobil cihazda belirgin takılmaya dönüşebilir. Bu nedenle ölçüm disiplininde cihaz sınıfı farkını özellikle korumak gerekir.

Önce görünür tepki vermenin önemi

Main thread blocking azaltma işinde en değerli düşünce biçimlerinden biri şudur: kullanıcı komut verdiğinde önce küçük ama görünür bir yanıt üretmek. Panel açılacaksa kabı hemen görünür kılmak, sekme değişecekse aktif durumu hızla değiştirmek, filtre uygulanacaksa yükleniyor halini geciktirmeden göstermek kullanıcıya “komut işlendi” hissi verir.

Bu yaklaşım gecikmeyi gizlemek için değil, ana iş parçacığı üzerindeki baskıyı daha yönetilebilir hale getirmek için işe yarar. Görünür tepki erken verildiğinde pahalı işin tamamı tek bir kritik ana yığılmaz. Böylece kullanıcı deneyimi daha akıcı kalır ve ekip hangi görevin gerçekten blok yarattığını daha net ayırabilir.

Üçüncü tarafların özel riski

Çünkü çoğu zaman ekip kontrolünün dışında ama aynı main thread üzerinde çalışırlar. Sohbet araçları, reklam kodları, izleme etiketleri ve deney platformları görünürde küçük ekler gibi başlar, sonra kritik anda ana iş parçacığına ortak olur.

Sorun yalnızca ilk yükte çalışmaları değildir. Bazıları etkileşim geldiğinde yeni görev başlatır, DOM tarar, olay dinler ya da yeniden ölçüm yapar. Bu da özellikle yoğun arayüz akışlarında kullanıcı hissini bozar.

Bu nedenle performans incelemesinde “bizim kodumuz mu ağır” sorusunun yanına “bizim adımıza kimler çalışıyor” sorusunu da koymak gerekir.

Sorunu kalıcı azaltacak disiplin

Main thread blocking tek seferlik temizlikle kapanmaz. Yeni kampanya modülü, yeni filtre paneli, yeni izleme kodu ya da yeni animasyon eklendikçe ana iş parçacığına yeni maliyet yazılır. Eğer ekip bunu düzenli izlemiyorsa gecikme geri döner.

Faydalı yaklaşım, kritik etkileşimleri ayrı ayrı takip etmektir. Hangi akışta gecikme var, hangi görev kümeleri bunu üretiyor, değişiklik sonrası hangi ölçüm düzeldi; bunlar kayıt altına alındığında hem teknik öncelikler netleşir hem de yanlış iyileştirme ihtimali azalır.

Sonuçta main thread blocking azaltma işi, “daha az kod yazalım” kadar basit değildir. Tarayıcının hangi anda neyle meşgul olduğunu anlamak ve kullanıcı komutuna yer açmakla ilgilidir. Bu bakış yerleştiğinde etkileşim performansı da daha öngörülebilir hale gelir.

Ana iş parçacığı boş değilse arayüz hızlı görünse bile akıcı hissettirmez. Gecikmenin görünmeyen yüzü çoğu zaman burada başlar.

Main thread blocking sorununu azaltmanın en güçlü yolu, işi sadece küçültmek değil, kritik anda daha hafif ve daha bölünmüş çalışacak şekilde yeniden düzenlemektir.