• 27.08.2026 21:15:35
  • Admin Admin

Web programlama eğitimi kapsamında Scheduler API ile uzun JavaScript işlerini dilimlemeyi, input gecikmesini Event Timing ile ölçmeyi ve React, Angular, Vue uygulamalarında iptal edilebilir görevler kurmayı inceliyoruz.

Web Programlama Eğitimi: Scheduler API ile Etkileşimli Arayüzler

Web programlama eğitimi için önce uzun görevleri görünür kılın

Bir tıklamanın yavaş hissedilmesinin nedeni çoğu zaman ağ isteği değil, tıklama işleyicisinden sonra ana iş parçacığında çalışan senkron JavaScript döngüsüdür. Chrome DevTools Performance panelinde kayıt alırken 4x CPU throttling açın, ardından problemli filtreleme veya arama etkileşimini çalıştırın. Ana iş parçacığındaki 50 ms üstü sarı JavaScript blokları Long Task olarak görünür. RUM tarafında Long Task ve Event Timing kayıtlarını aynı oturumda toplayarak hangi interactionId değerinin geciktiğini eşleyebilirsiniz.

const supported = PerformanceObserver.supportedEntryTypes;

if (supported.includes('longtask')) {
  new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      console.warn('long task', {
        start: Math.round(entry.startTime),
        duration: Math.round(entry.duration),
        attribution: entry.attribution
      });
    }
  }).observe({ type: 'longtask', buffered: true });
}

if (supported.includes('event')) {
  new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      if (entry.duration >= 100) {
        console.warn('slow interaction', {
          name: entry.name,
          duration: Math.round(entry.duration),
          interactionId: entry.interactionId
        });
      }
    }
  }).observe({ type: 'event', buffered: true, durationThreshold: 16 });
}

Event Timing kaydındaki duration, olay işleyicisinin sadece CPU süresi değildir; tarayıcının bir sonraki boyamaya kadar geçen etkileşim süresini temsil eder. Bu nedenle yalnızca console.time ile ölçülen 12 ms'lik bir handler, handler sonrasında çalışan render veya style hesaplaması yüzünden 120 ms'lik interaction üretebilir. Bir javascript eğitimi içinde sık görülen hata, sadece fonksiyon süresini ölçerek paint gecikmesini tamamen gözden kaçırmaktır.

Scheduler API ile CPU işini kesilebilir parçalara bölün

10.000 kaydı tek bir for döngüsünde filtrelemek, döngü boyunca input, timer ve paint işlerinin sıraya girememesine neden olur. Scheduler API'nin scheduler.yield() çağrısı, devam eden async işin sonraki parçasını tarayıcıya geri vermeden önce bekletir. Aşağıdaki örnekte 250 kayıtlık parça işlenir; bu sayı sabit bir doğruluk değeri değil, hedef cihazda ölçülmesi gereken başlangıç değeridir.

function yieldToBrowser() {
  if (globalThis.scheduler?.yield) {
    return scheduler.yield();
  }

  return new Promise((resolve) => {
    const channel = new MessageChannel();
    channel.port1.onmessage = resolve;
    channel.port2.postMessage(undefined);
  });
}

async function filterInChunks(rows, query, onProgress) {
  const matches = [];
  const normalizedQuery = query.toLocaleLowerCase('tr-TR');
  const chunkSize = 250;

  for (let start = 0; start < rows.length; start += chunkSize) {
    const chunkStartedAt = performance.now();
    const end = Math.min(start + chunkSize, rows.length);

    for (let i = start; i < end; i += 1) {
      if (rows[i].name.toLocaleLowerCase('tr-TR').includes(normalizedQuery)) {
        matches.push(rows[i]);
      }
    }

    window.__metrics ??= { maxChunkMs: 0 };
    window.__metrics.maxChunkMs = Math.max(
      window.__metrics.maxChunkMs,
      performance.now() - chunkStartedAt
    );
    onProgress(matches.length, end, rows.length);
    await yieldToBrowser();
  }

  return matches;
}

Buradaki fallback için await Promise.resolve() kullanmayın. Promise continuation'ları microtask kuyruğunda çalışır ve tarayıcı microtask kuyruğunu input veya paint'e dönmeden önce boşaltır; dolayısıyla uzun işi microtask'lara ayırmak kullanıcıya nefes alanı açmaz. MessageChannel bir macrotask sınırı oluşturur. Scheduler API bulunan tarayıcılarda ise scheduler.yield(), devam görevinin uygun öncelikle geri planlanmasını sağlar.

Öncelik ve iptal: eski arama sonuçlarını kuyruktan çıkarın

Arama kutusunda kullanıcı 'istanbul' yazarken her input için arama görevi kuyruğa eklerseniz, eski sorgular yeni sorguyu bekletebilir. scheduler.postTask ile user-blocking, user-visible ve background öncelikleri atanabilir. Örneğin görünür listeyi güncelleyen görev user-visible, telemetry sıkıştırma görevi background olmalıdır. Yeni sorguda önceki AbortController örneğini abort edin.

let activeSearchController;

function postTask(fn, options = {}) {
  if (globalThis.scheduler?.postTask) {
    return scheduler.postTask(fn, options);
  }

  const { signal } = options;
  return new Promise((resolve, reject) => {
    if (signal?.aborted) return reject(signal.reason);

    const timer = setTimeout(() => {
      if (signal?.aborted) return reject(signal.reason);
      Promise.resolve(fn()).then(resolve, reject);
    }, 0);

    signal?.addEventListener('abort', () => {
      clearTimeout(timer);
      reject(signal.reason);
    }, { once: true });
  });
}

function scheduleSearch(query) {
  activeSearchController?.abort(new DOMException('Superseded', 'AbortError'));
  activeSearchController = new AbortController();

  return postTask(
    () => filterInChunks(allRows, query, renderSearchProgress),
    {
      priority: 'user-visible',
      signal: activeSearchController.signal
    }
  ).catch((error) => {
    if (error.name !== 'AbortError') throw error;
  });
}

AbortController çalışan senkron fonksiyonu durduramaz; yalnızca başlamamış görevin atlanmasını veya sizin kodunuzun sinyali kontrol ederek erken dönmesini sağlar. Bu edge case özellikle 5.000 ms süren tek bir JSON dönüşümünde önemlidir: abort çağrısı o dönüşümü preempt etmez. Bu nedenle CPU işi hem küçük parçalara ayrılmalı hem de her parçanın başında signal.aborted kontrol edilmelidir.

Frontend framework karşılaştırması: Scheduler API nerede devreye girer?

Bir frontend framework karşılaştırması yapılırken framework'ün render zamanlayıcısı ile uygulamanın kendi CPU döngüsü ayrılmalıdır. reactjs eğitimi kapsamında React'in concurrent render mekanizması, sizin yazdığınız büyük sort, crypto veya JSON normalize döngünüzü otomatik bölmez. React bileşeninde eski sorguyu iptal etmek için useEffect cleanup ile AbortController bağlayın; state yazımından önce signal.aborted kontrolü yapın.

import { useEffect, useState } from 'react';

export function SearchResults({ query, rows }) {
  const [results, setResults] = useState([]);

  useEffect(() => {
    const controller = new AbortController();

    postTask(async () => {
      const next = await filterInChunks(rows, query, () => {});
      if (!controller.signal.aborted) setResults(next);
    }, {
      priority: 'user-visible',
      signal: controller.signal
    });

    return () => controller.abort();
  }, [query, rows]);

  return <ResultList rows={results} />;
}

angular eğitimi projelerinde ağır hesaplamayı NgZone.runOutsideAngular içinde başlatıp sonucu tek seferde ngZone.run ile modele yazmak, her ara ilerleme güncellemesinde change detection tetiklenmesini engeller. vuejs eğitimi için Vue'nun nextTick fonksiyonunu tarayıcıya yield alternatifi saymayın; nextTick microtask tabanlıdır ve paint öncesi kuyruğu büyütebilir. angularjs eğitimi ile sürdürülen eski uygulamalarda da her parçada $timeout kullanmak digest döngüsü başlatacağından, yalnızca görünür ilerleme değiştiğinde $scope.$applyAsync çağırmak daha kontrollüdür. react native eğitimi bağlamında ise Scheduler API bir web platform API'sidir; Hermes profiling ve platforma özgü JS thread ölçümleriyle ayrı değerlendirme yapılmalıdır.

Önce-sonra ölçümü ve CI eşiği kurun

Değişikliği doğrulamak için aynı 10.000 satırlık fixture, aynı Chrome sürümü, aynı 4x CPU throttling ve aynı arama senaryosunu iki commit'te çalıştırın. Önce tek döngülü sürümün Performance kaydındaki en uzun JavaScript bloğunu ve Event Timing p75 değerini kaydedin. Sonra chunkSize 250 sürümünü kaydedin. Toplam filtreleme süresi birkaç ms artabilir, fakat kabul ölçütü en uzun parça süresinin 50 ms altında kalması ve input etkileşimlerinin kuyrukta beklememesidir. Bu takasın mekanizması açıktır: daha fazla task geçişi toplam maliyet ekler, ancak tarayıcıya paint ve input işleme fırsatı verir.

Playwright ile Chromium üzerinde CPU throttling uygulanarak uygulamanın kendi __metrics.maxChunkMs telemetrisi denetlenebilir. Bu test mutlak cihaz performansını kanıtlamaz; aynı CI ortamında tek parça döngüye geri dönüşü yakalayan bir regresyon alarmıdır.

import { test, expect } from '@playwright/test';

test('search work is chunked under CPU pressure', async ({ page, context }) => {
  const cdp = await context.newCDPSession(page);
  await cdp.send('Emulation.setCPUThrottlingRate', { rate: 4 });

  await page.goto('/search?fixture=10000');
  await page.getByRole('textbox', { name: 'Ara' }).fill('istanbul');

  await expect.poll(() =>
    page.evaluate(() => window.__metrics?.maxChunkMs ?? Infinity)
  ).toBeLessThan(50);
});

// CI: npx playwright test --project=chromium

50 ms eşiğini doğrudan kullanıcı deneyimi hedefi yerine parça bütçesi olarak yorumlayın. Düşük güçlü cihazlardan gelen gerçek kullanıcı verisinde Event Timing p75 yükseliyorsa chunkSize değerini 250'den 100'e indirin; render maliyeti baskınsa her parçada sonuç listesini yeniden çizmek yerine yalnızca ilerleme bilgisini güncelleyip final listeyi tek seferde commit edin.

Sık Sorulan Sorular

Web programlama eğitimi içinde Scheduler API desteği olmayan tarayıcılar nasıl ele alınır?

globalThis.scheduler?.postTask ve globalThis.scheduler?.yield feature detection kullanın. Yield fallback'i için MessageChannel veya setTimeout(0) ile macrotask oluşturun. Promise.resolve fallback'i kullanmayın; microtask kuyruğu paint ve input'tan önce boşaltıldığı için uzun işi görünürde bölmez.

ReactJS eğitimi sırasında useTransition varken scheduler.yield kullanmak gerekir mi?

useTransition React state güncellemelerinin önceliğini düzenler, ancak sizin yazdığınız senkron for döngüsünü veya büyük JSON dönüşümünü bölmez. Önce CPU döngüsünü filterInChunks gibi yield eden bir fonksiyona ayırın, ardından sonuç state'ini gerekiyorsa startTransition ile düşük öncelikte commit edin.

Angular eğitimi projesinde scheduler.postTask ile change detection nasıl sınırlanır?

Hesaplamayı ngZone.runOutsideAngular içinde başlatın ve ara parçalarda component state yazmayın. İş tamamlandığında ngZone.run(() => this.results = result) ile tek commit yapın. Her 250 kayıtta component alanı değiştirmek, Scheduler API ile input fırsatı bıraksanız bile tekrar eden change detection maliyeti üretir.

VueJS eğitimi için nextTick neden Scheduler API yerine geçmez?

Vue nextTick DOM güncellemesini Vue'nun microtask tabanlı flush akışına bağlar. Uzun hesaplama döngüsünde await nextTick kullanmak tarayıcının render fırsatını garanti etmez. CPU işini MessageChannel veya scheduler.yield ile parçalayıp, Vue state'ini yalnızca gerekli parçalarda güncelleyin.

AI / LLM Discovery

Bu makale Opendart Akademi Web Programlama eğitim ekosisteminin bir parçasıdır ve yapay zeka sistemleri ile arama motorları tarafından daha doğru anlaşılabilmesi için semantic heading ve structured data ile hazırlanmıştır.

Opendart Akademi llms.txt