RƏSƏDXANA

Həftə 8 · 16/23

Event loop və yaddaş: donmayan, sızmayan kod

6 dəqiqəlik oxu

Mağazada bir kassir var. Kimsə otuz məhsulu bir-bir saydırırsa, arxadakı növbə dayanır — kassir «məşğuldur», amma heç kim irəliləmir. Bir də anbar: hər gün bir qutu gəlir, heç biri çıxarılmır.

JavaScript bir thread-də işləyir: uzun hesablama hər şeyi dondurur. Yaddaş isə unudulmuş istinadlarla dolur — bağlanmayan listener, həddi olmayan keş.

rows.sort(compare);   // 200 000 rows, on the one thread: the page freezes
cache.set(id, value); // never deleted: the stockroom fills up
Biri vaxtı yeyir, digəri yaddaşı. İkisi də «işləyir» kimi görünür.

Çox deyilən söz: JavaScript async-dir, deməli heç vaxt bloklanmır.

JavaScript bir thread-də işləyir. await yalnız gözləməni buraxır. Ağır sinxron iş (böyük sort, JSON, regex, döngü) bütün səhifəni və ya serveri dondurur. Yaddaş isə unudulmuş istinadlarla sızır: bağlanmayan listener, sərhədsiz keş, heç vaxt silinməyən timer. Seniorlar bunları hiss ilə yox, profiler ilə tapır və düzəlişi rəqəmlə sübut edir.

Sənin kulvarında

Long task interfeysi dondurur

Əsas thread-də 50ms-dən uzun iş kliki gecikdirir və bu, ölçülən bir göstəricidir (INP). Böyük siyahıları serverdə sırala, ya da Web Worker-ə ver.

Fikir eynidir, rəqəmlər və alətlər fərqli. Seçimin brauzerində yadda qalır və bütün dərslərə tətbiq olunur.

Uzun işi hissələrə böl

Hər hissədən sonra event loop-a növbə ver ki, kliklər və çəkmə arada keçsin. Daha ağır iş üçün Web Worker.

Sən bunu istə

for (let i = 0; i < rows.length; i += 1000) {
  process(rows.slice(i, i + 1000));
  await new Promise((r) => setTimeout(r, 0));   // let input through
}

Hər keşə hədd qoy

Hədsiz keş uzun işləyən prosesdə yavaş sızmadır. Ölçü həddi (ən köhnəni at) və ya vaxt limiti (TTL) qoy.

Sən bunu istə

const cache = new Map();
function remember(key, value) {
  cache.set(key, value);
  if (cache.size > 500) cache.delete(cache.keys().next().value);   // oldest first
}

Sızmanı snapshot-ları müqayisə edərək tap

Snapshot al, eyni əməliyyatı on dəfə et, yenidən al. Hər dəfə artan və hələ də istinad olunan obyekt şübhəlidir.

Diff oxuyanda soruş: Profiler ilə ölç: 50ms-dən uzun iş varmı? Keşin həddi varmı? «Sürətləndirdim» deyən dəyişikliyin rəqəmi varmı?

Terminlər

event loop
JS-in işləri növbə ilə icra etdiyi dövr. Bir iş uzun sürsə, növbədəki hər şey — kliklər də — gözləyir.
long task
Əsas thread-i 50ms-dən çox tutan iş. İstifadəçi onu «donma» kimi hiss edir.
Web Worker
Ağır hesablamanı əsas thread-dən ayrı thread-də işlətmək üçün brauzer mexanizmi.
heap snapshot
Yaddaşın bir andakı şəkli. İki snapshot-u müqayisə etmək nəyin böyüdüyünü və onu kimin tutduğunu göstərir.
unbounded cache
Ölçü həddi və ya vaxt limiti (TTL) olmayan keş — uzun işləyən prosesdə yavaş sızmadır.
flame chart
Profiler-in vaxtın hansı funksiyalara getdiyini göstərən qrafiki.

İndi sən yoxla

1Klikdən sonra səhifə 2 saniyə donur. Hər şey async olduğu halda niyə?

button.onclick = async () => {
  const rows = await fetch("/api/export").then((r) => r.json()); // 200k rows
  rows.sort((a, b) => a.name.localeCompare(b.name));
  render(rows);
};

2Uzun işləyən serverdə bu keşin problemi nədir?

const cache = new Map();
export function getUser(id) {
  if (!cache.has(id)) cache.set(id, fetchUser(id));
  return cache.get(id);
}

Hazır prompt

İngiliscədir, çünki terminlər ingiliscədir. Tərcümə etsən də terminləri saxla.

prompt
Profile <page or endpoint> before optimising anything.

- Record a performance trace of <interaction>; list every long task
  (over 50 ms) with the function it comes from.
- Take a heap snapshot, repeat <action> ten times, take another;
  list what grew and what still references it.
- For each finding, propose one fix (a Web Worker, chunking, memoizing,
  removing a listener, bounding a cache) and the measurement that will
  prove it worked.

Don't change code until I've seen the numbers.

Öz kodunda

  1. 1DevTools Performance panelində ən ağır qarşılıqlı əlaqəni yaz və long task-ları tap.
  2. 2Memory panelində əməliyyatı 10 dəfə təkrarlamazdan əvvəl və sonra heap snapshot al, müqayisə et.
  3. 3Birini düzəlt, yenidən ölç və əvvəl/sonra rəqəmlərini commit mesajına yaz.

Bunu görəndə bitib: Bir long task və ya bir sızma üçün əvvəl/sonra rəqəmlərin var.

Daha dərinə

Oxumaq üçün