Javascript İle Tarayıcı Tabanlı Bir Dinamik Görev Zamanlayıcı Nasıl Yapılır?

Ön Hazırlık ve Gereksinimler

Bu projeyi hayata geçirmek için herhangi bir dış kütüphaneye ihtiyacınız yoktur. Modern bir tarayıcı (Chrome, Firefox, Edge veya Safari) ve temel düzeyde JavaScript bilgisi yeterlidir. Çalışmalarınızı bir metin editörü (VS Code gibi) üzerinde yapabilir ve herhangi bir web sunucusuna ihtiyaç duymadan doğrudan HTML dosyalarınızı tarayıcıda açarak test edebilirsiniz.

Geliştirme sürecinde ES6+ sözdizimini (söz dizimi kuralları) kullanacağız. Özellikle Promise, async/await ve class yapıları, kodumuzun okunabilirliğini ve yönetilebilirliğini artıracaktır. Tarayıcı konsolunu (F12) aktif kullanarak hata ayıklama süreçlerini hızlandırmanızı öneririm.

Temel Zamanlama Mekanizması: setTimeout ve setInterval

JavaScript'te zamanlama denilince akla ilk gelen setTimeout ve setInterval fonksiyonlarıdır. Ancak dinamik bir zamanlayıcı için bu iki fonksiyonun sınırlarını bilmek gerekir. setTimeout tek seferlik bir gecikme sağlarken, setInterval belirli aralıklarla tekrarlanan işlemler için kullanılır.

Aşağıdaki örnekte, dinamik görevlerimizi yönetmek için bir sınıf yapısının temelini atıyoruz:

class GorevZamanlayici {
    constructor() {
        this.gorevler = [];
    }

    ekle(id, callback, gecikme) {
        const timerId = setTimeout(() => {
            callback();
            this.sil(id);
        }, gecikme);

        this.gorevler.push({ id, timerId });
    }

    sil(id) {
        this.gorevler = this.gorevler.filter(g => g.id !== id);
    }
}

Bu kod bloğu, görevleri bir dizi içerisinde tutarak onları takip etmemizi sağlar. ekle metodu ile bir görev tanımlanır ve görev süresi dolduğunda otomatik olarak listeden temizlenir.

Dinamik Görev Yönetimi İçin Sınıf Yapısı

Dinamik bir sistemde görevleri sadece eklemek yetmez; aynı zamanda durdurabilmeli, güncelleyebilmeli ve durumlarını kontrol edebilmeliyiz. Bunun için görevlerimize benzersiz kimlikler (ID) atamamız ve onları bir nesne yapısında tutmamız en sağlıklı yöntemdir.

Aşağıdaki gelişmiş sınıf yapısı, görevleri yönetmek için daha esnek bir yapı sunar:

class DinamikZamanlayici {
    constructor() {
        this.gorevHavuzu = new Map();
    }

    gorevBaslat(id, fonksiyon, milisaniye) {
        if (this.gorevHavuzu.has(id)) {
            console.warn(`${id} ID'li görev zaten çalışıyor.`);
            return;
        }
        const timer = setInterval(fonksiyon, milisaniye);
        this.gorevHavuzu.set(id, timer);
    }

    gorevDurdur(id) {
        if (this.gorevHavuzu.has(id)) {
            clearInterval(this.gorevHavuzu.get(id));
            this.gorevHavuzu.delete(id);
        }
    }
}

Burada Map veri yapısını kullandık. Map, anahtar-değer çiftlerini saklamak için Object yapısına göre daha performanslıdır ve özellikle dinamik ekleme/silme işlemlerinde daha temiz bir API sunar.

Görevlerin Tarayıcı Depolama (LocalStorage) ile Kalıcı Hale Getirilmesi

Tarayıcı tabanlı bir uygulamada sayfa yenilendiğinde görevlerin kaybolmaması için localStorage (yerel depolama) kullanmalıyız. Bu, kullanıcı deneyimi açısından kritik bir adımdır. Ancak, localStorage sadece metin (string) verisi saklayabildiği için verilerimizi JSON.stringify ile dönüştürmemiz gerekir.

function goreviKaydet(gorev) {
    const mevcutGorevler = JSON.parse(localStorage.getItem('gorevler') || '[]');
    mevcutGorevler.push(gorev);
    localStorage.setItem('gorevler', JSON.stringify(mevcutGorevler));
}

Bu fonksiyon, her yeni görevi tarayıcının yerel hafızasına ekler. Uygulama her açıldığında bu verileri okuyarak zamanlayıcıyı tekrar başlatabilirsiniz.

Kritik Güvenlik Uyarısı: localStorage içerisinde asla hassas kullanıcı verilerini (şifre, token, kişisel bilgiler) şifrelenmemiş halde saklamayın. XSS (Cross-Site Scripting) saldırılarına karşı verilerinizi her zaman sanitize edin (temizleyin).

Performans ve Karşılaştırma Tablosu

Zamanlayıcı yöntemlerini seçerken projenizin ihtiyaçlarına göre karar vermelisiniz. Aşağıdaki tablo, farklı yöntemlerin avantaj ve dezavantajlarını özetlemektedir.

Yöntem Avantaj Dezavantaj
setTimeout Düşük kaynak tüketimi Tek seferlik işlemler için uygun
setInterval Sürekli tekrarlayan işlemler Hata birikimi (drift) yaşanabilir
requestAnimationFrame Yüksek performans (UI için) Sadece ekran yenileme hızıyla sınırlı

Hata Yönetimi ve Debug İpuçları

Dinamik bir zamanlayıcıda en sık karşılaşılan hata, zamanlayıcıların temizlenmemesi (memory leak - bellek sızıntısı) sonucunda tarayıcının yavaşlamasıdır. Her setInterval için mutlaka bir clearInterval çağrısı yapıldığından emin olun.

Aşağıdaki kod, görevlerin güvenli bir şekilde temizlenmesini sağlar:

const tumGorevleriTemizle = (zamanlayici) => {
    for (let id of zamanlayici.gorevHavuzu.keys()) {
        zamanlayici.gorevDurdur(id);
    }
    console.log("Tüm görevler başarıyla durduruldu.");
};

Debug yaparken console.table() kullanarak gorevHavuzu içerisindeki aktif görevleri görselleştirebilirsiniz. Bu, hangi görevin neden çalışmadığını anlamanızı kolaylaştırır.

Sıkça Sorulan Sorular

Dinamik zamanlayıcılar tarayıcı arka planda iken çalışır mı?

Modern tarayıcılar, pil tasarrufu için arka plandaki sekmelerde setInterval sürelerini kısıtlar. Eğer arka planda kesin çalışması gereken bir işlem varsa, Web Workers API'sini incelemenizi öneririm.

Zamanlayıcıda milisaniye hassasiyeti ne kadar güvenilir?

JavaScript'in zamanlama fonksiyonları "tam" zamanında çalışmayı garanti etmez. Eğer milisaniyelik hassasiyet gerekiyorsa, performansın yoğun olduğu durumlarda sapmalar yaşanabilir.

LocalStorage verisi ne kadar süre saklanır?

localStorage verileri, kullanıcı tarayıcı geçmişini silene veya siz kodunuzla veriyi temizleyene kadar kalıcıdır. sessionStorage ise sadece sekme açık kaldığı sürece veriyi tutar.

Birden fazla zamanlayıcı aynı anda çalışabilir mi?

Evet, JavaScript tek iş parçacıklı (single-threaded) çalışsa da, Event Loop sayesinde yüzlerce zamanlayıcıyı eşzamanlı gibi yönetebilir.

Güvenlik açısından nelere dikkat etmeliyim?

Kullanıcıdan alınan verileri zamanlayıcı fonksiyonlarında doğrudan çalıştırmayın. eval() kullanımından kesinlikle kaçının, çünkü bu çok ciddi güvenlik açıklarına yol açar.

Web Worker ile Zamanlayıcıyı Arka Plana Taşıma

Tarayıcı tabanlı zamanlayıcılar, ana iş parçacığında (main thread) çalıştıkları için yoğun DOM manipülasyonu veya ağır hesaplamalar sırasında "donma" yaşayabilirler. Zamanlayıcınızı bir Web Worker içerisine taşımak, uygulamanızın kullanıcı arayüzünden bağımsız olarak arka planda kararlı bir şekilde çalışmasını sağlar.

Web Worker Yapılandırması

Öncelikle, zamanlayıcı mantığınızı içeren worker.js dosyasını oluşturmalısınız. Bu dosya, ana sayfadan gelen mesajları dinler ve belirlenen zaman aralıklarında ana sayfaya geri bildirim gönderir.

// worker.js
self.onmessage = function(e) {
    const { action, delay } = e.data;
    if (action === 'start') {
        setInterval(() => {
            self.postMessage({ type: 'tick', time: Date.now() });
        }, delay);
    }
};

Ana İş Parçacığı ile İletişim

Ana dosyanızdan bu worker'ı başlatmak için aşağıdaki yapıyı kullanabilirsiniz. Bu yöntem, uygulamanızın performansını doğrudan artırır ve zamanlama hassasiyetini korur.

// main.js
const worker = new Worker('worker.js');

worker.postMessage({ action: 'start', delay: 1000 });

worker.onmessage = function(e) {
    if (e.data.type === 'tick') {
        console.log('Görev tetiklendi:', new Date(e.data.time).toLocaleTimeString());
    }
};

Zamanlayıcı İçin Birim Testi Stratejileri

Zamanlayıcı tabanlı uygulamalarda "zaman" kavramı test edilmesi en zor unsurlardan biridir. Gerçek zamanı beklemek yerine, Jest veya Vitest gibi kütüphanelerin sunduğu Fake Timers özelliğini kullanarak test süreçlerinizi hızlandırabilirsiniz.

Test Senaryosu Örneği

Aşağıdaki örnekte, zamanlayıcının belirli bir süre sonra gerçekten tetiklenip tetiklenmediğini nasıl doğrulayacağınızı görebilirsiniz.

// scheduler.test.js
import { describe, it, expect, vi } from 'vitest';

describe('Zamanlayıcı Testi', () => {
    it('belirlenen sürede tetiklenmeli', () => {
        vi.useFakeTimers();
        const callback = vi.fn();
        
        // Zamanlayıcıyı başlat
        setTimeout(callback, 5000);
        
        // Zamanı ileri sar
        vi.advanceTimersByTime(5000);
        
        expect(callback).toHaveBeenCalledTimes(1);
        vi.useRealTimers();
    });
});

İleri Seviye Optimizasyon İpuçları

Uygulamanızın ölçeklenebilirliğini artırmak için şu teknikleri göz önünde bulundurmalısınız:

  • RequestAnimationFrame Kullanımı: Görsel güncellemeler içeren görevler için setInterval yerine requestAnimationFrame tercih edin. Bu, tarayıcının ekran yenileme hızıyla uyumlu çalışmasını sağlar.
  • Görev Önceliklendirme: Kuyruğunuzdaki görevleri önem derecesine göre sıralayın. Kritik görevleri PriorityQueue yapısı ile yönetin.
  • Bellek Yönetimi: Zamanlayıcı durdurulduğunda mutlaka clearInterval veya clearTimeout çağırarak referansları temizleyin, aksi takdirde bellek sızıntıları (memory leaks) oluşabilir.
Not: Çok sayıda eşzamanlı zamanlayıcı çalıştırmak, mobil cihazlarda pil tüketimini artırabilir. Mümkün olduğunca görevleri tek bir merkezi zamanlayıcı üzerinden yönetmek, kaynak kullanımı açısından en verimli yöntemdir.

Sonuç

JavaScript ile tarayıcı tabanlı bir dinamik görev zamanlayıcı oluşturmak, hem temel dil yeteneklerinizi geliştirir hem de kullanıcı odaklı uygulamalar yapmanıza olanak tanır. Bu rehberde öğrendiğiniz sınıf tabanlı yapı, localStorage kullanımı ve hata yönetimi prensipleri, profesyonel projeleriniz için sağlam bir temel oluşturacaktır.

Sorumluluk Reddi: Bu makaledeki kod örnekleri eğitim amaçlıdır. Üretim ortamında (production) kullanmadan önce, kodunuzu güvenlik testlerinden geçirin ve XSS açıklarına karşı giriş verilerini mutlaka doğrulayın.

Bir sonraki adım olarak, bu zamanlayıcıyı bir Web Worker içerisine taşıyarak ana iş parçacığını (main thread) nasıl rahatlatabileceğinizi araştırmanızı öneririm. Bu, uygulamanızın daha karmaşık işlemlerde bile donmadan çalışmasını sağlayacaktır.

Bu yazıya tepkinizi paylaşın:
Emre Güneş

Teknik çözümler ve ev gereçleri kullanımı konusunda deneyimli bir editörüm. Kullanıcı hatalarını minimize eden, yalın ve çözüm odaklı rehberler hazırlıyorum.

Yorumlar (0)

Yorum Yaz