Javascript-də "Worker"-lər

Zaur HamidovZaur Hamidov
6 min read

Javascript bir çox cəhətdən zəngin dildir. Onun işə yarar bir çox özəlliyi var , digər istifadəçilər tərəfindən yaradılan bir çox kitabxanası var , həm client tərəfdə , həm də server tərəfdə ( node.js , deno , bun kimi runtime - ların köməyilə ) işləyə bilir , yazılan kod bir çox platformalarda işləyir ( web , mobile , smart cihazlar , oyun proqramlaşdırılması , Aİ , data visualization və s ).

Tək thread-lı ( single threaded ) Javascript

Bütün bunlara baxmayaraq Javascript-in tək thread-lı olması bəzən problemlər yaradır. Məsələn , təsəvvür edin ki siz bir proqram hazılayırsız və onun daxilində mürəkkəb bir iş görən funksiya var. Bu funksiya işə düşən zaman bitməsi məsələn 10 saniyə çəksin. Burdakı problem odur ki bu funksiyanı işə salan zaman main thread ( loru dildə desək bütün işlərin baş verdiyi yer ) bloklanır və istifadəçi bu 10 saniyəni gözləməlidir bu isə elə də yaxşı deyil. Siz fikirləşə bilərsiz ki , biz bunu asinxron olaraq çağıra bilərik ki main thread bloklanmasın ancaq asinxron olaraq çağıranda belə yenə də proses main thread-da gedəcək deyə bloklanma olacaq. Javascriptdə asinxronluq sadəcə funksiyanın işə salınması üçündür , onun işə salındıqdan sonra icra edilməsi ( execution ) asinxron deyil və main thread-da baş verir. Məsələn:

function murekkebFunksiya(){
    for ( let x = 0; x < 1000000000; x++ ){
        x ** 2;
    }
    console.log('bitdi');
}

function test(){
    console.log('a');
    Promise.resolve().then(murekkebFunksiya);
    console.log('b')
}

test();

// a
// b
// bitdi

Burada ilk olaraq 'a' və 'b' ekrana verilir və 'then' daxilindəki callback microtask növbəsində olduğu üçün onlardan sonra ekrana verilir. Ancaq 'b' ekrana verildikdən sonra istifadəçi "murekkebFunksiya' - nın nəticəsini 'gözləyir'. Çünki bu funksiyanın nəticəsi main thread-da hesablanır və bu müddət ərzində main thread bloklandığı üçün funksiyanın icrası bitənə qədər javascript ilə bağlı proseslər işləməyəcək.

Web worker-lər problemimizi həll edir

Bizə elə bir vasitə lazımdır ki , icra edilmə ( execution ) main thread-da yox başqa bir thread-da baş versin və nəticə bəlli olanda bizə ( daha doğrusu main thread-a ) məlumat göndərsin. Web workers məhz bunun üçündür. biz web worker istifadə etməklə hər hansı bir əməliyyatı 'arxa fonda' icra edə bilərik və əməliyyat bitəndə , onun cavabını ala bilərik Burada 'arxa fon' dedikdə , main thread-dan əlavə bir thread yaradıb , kodu orada işə salmaq nəzərdə tutulur. Kompüter arxitekturasında buna multithreading deyilir. Kod yazmağa keçməzdən əvvəl qısa formada worker növlərinə baxaq:

  • Web Worker ( Ağır funksiyaların və ya kodların başqa bir thread-da işlədilməsi üçün )

    • Dedicated Worker ( yalnız onu yaradan script üçün əlçatandır )

    • Shared Worker ( bir neçə script ,pəncərə , iframe və hətta worker üçün əlçatandır )

  • Service Worker ( Şəbəkə sorğularına müdəxilə edib , bəzi APİ-lar ilə daha yaxşı istifadəçi təcrübəsi yaratmaq üçün )

Worker-lər bizə bir çox faydalı APİ - lər ilə işləməyə imkan verir bunlara dair bəzi nümunələr:

  1. Background Fetch API

  2. Background Synchronization API

  3. Fetch API

  4. File API

  5. Notifications API

  6. Push API

  7. Server-Sent Events

  8. Web Periodic Background Synchronization API

Tam siyahi üçün bu linkə keçid edə bilərsiz: Worker daxilində mövcud olan api siyahısı

💡
Web workerləri necə işlədəcəyimizə keçməzdən əvvəl qısa bir nüansı vurğulayım: biz bu yazımızda dedicated worker ilə işləyəcəyik. buna səbəb onun shared worker-dən daha asan olması və worker ilə script arasında daha sadə məlumat ötürməyidir. Həmçinin biz bu yazımızda worker-lərin bütün imkanlarına ( və APİ-lara ) nəzər yetirməyəcəyik , sadəcə worker-lərdən istifadəyə dair qısa giriş dərsi verəcəyik. Sonrakı yazımızda , Worker-lərə daha ətraflı baxış edəcəyik və onların digər imkanlarından da bəhs edəcəyik

Web Worker - dən istifadə edərək , əməliyyatların 'arxa fonda' icra edilməsi

Worker istifadəsi üçün aşağıdakı addımları edirik:

  1. Feature detection ilə hazırkı mühitdə , worker-lərin dəstəkləndiyindən əmin oluruq

  2. Main Thread-da yeni worker yaradırıq

  3. Main Thread-dan bu worker-ə 'postMessage' metodu ilə data göndəririk

  4. Həmin worker scriptində , event listener ilə 'message' eventini 'dinləyərək' main thread-dan gələn dataları qəbul edirik və uyğun əməliyyatları yerinə yetiririk ( və ya onmessage event handler ilə )

Indi isə keçək kodlara:

💡
Workerlər-də dom access mümkün deyil , siz worker-lərdən birbaşa document node-lara müdəxilə edə bilməzsiz.

index.js faylı

/*    index.js      */

// feature detection ilƏ worker dəstəyini yoxlayırıq
if ( !window.Worker ){  // => ( əgər worker dəstəklənmirsə )

    // console-a məlumatı yazırıq
    console.log('Hazırkı mühit worker-ləri dəstəkləmir !');

}else{ // əks halda , yəni dəstəklənirsə

    // worker yaradırıq
    const worker = new Worker('worker.js');
    // postMessage ilə bu worker-ə data göndəririk
    worker.postMessage('main thread-dan mesaj');

}

worker.js faylı

// 'message' eventini 'dinləyirik' 
addEventListener('message', event => {

    // main thread-dan gələn datanı götürürük
    const mainThreadData = event.data;

    // bu datanı console-a veririk
    console.log( `mesaj : ${mainThreadData}`)
});

Bunu işə saldığınız zaman console-da mesaj : main thread-dan mesaj yazısını görəcəksiz.

Düzdür console-a main thread-dan gələn mesajl worker işlədərk ayrı bir thread içində console-a versək də əslində burada elə də qeyri-adi birşey etmədik. Ona görə də yazının əvvəlindəki mürəkkəb funksiyanı worker ilə ayrı bir thread-da icra edək:

index.js faylı

/*    index.js      */

// feature detection ilƏ worker dəstəyini yoxlayırıq
if ( !window.Worker ){  // => ( əgər worker dəstəklənmirsə )

    // console-a məlumatı yazırıq
    console.log('Hazırkı mühit worker-ləri dəstəkləmir !');

}else{ // əks halda , yəni dəstəklənirsə

    // worker yaradırıq
    const worker = new Worker('worker.js');

    //  Əvvəlki test funksiyasını yaradırıq
    function test(){

        // a və b -ni sinxron olaraq console-a veririk
        console.log('a');
        console.log('b');

        // worker-dən data göndəriləndə , həmin datanı console-a
        // vermək üçün main thread-da da 'message' eventini 'dinləyirik'
        worker.addEventListener("message", ({ data }) => {
            console.log(data);
        });

        // worker-ə 'start' scriptini göndəririk
        worker.postMessage('start');

    }

    // test funksiyasını işə salırıq
    test();

}

worker.js faylı

// 'message' eventini 'dinləyirik' 
addEventListener('message', event => {

    // main thread-dan gələn datanı götürürük
    const mainThreadData = event.data;

    // əgər göndərilən data 'start' string-inə bərabərdirsə , 
    // mürəkkəb funksiyanı işə sal
    if ( mainThreadData === 'start' ){
        murekkebFunksiya();
    }

});

function murekkebFunksiya(){

    for ( let x = 0; x < 1000000000; x++ ){
        x ** 2;
    }

    console.log('bitdi');

}

Bu kodları yazıb işə saldıqdan sonra əvvəlki kimi console-a a , b sonda isə 'bitdi' yazısı veriləcək. Ancaq bu zaman fərq ondadır ki , istifadəçi 'bitdi' yazısını gözləyərkən Uİ ( istifadəçi interfeysi ) aktiv qalacaq və istifadəçi digər əməliyyatlar yerinə yetirə biləcək ( click etmək , yazı yazmaq və s. ). Bu ona görə mümkün olur ki , mürəkkəb funksiya main thread-da yox , yeni bir thread-da işə düşür.

Web workerlərdən istifadə edərək bir çox məsələləri həll etmək olar. Onları necə istifadə edəcəyiniz sizin seçiminizdir. Bu yazıda worker-lərin istifadəsinə dair qısa məlumat verildi ancaq əvvəldə qeyd etdiyim kimi worker-lər haqqında danışılmalı çox mövzu var ( məs: structure clone algorithm , worker ilə transferable object istifadəsi , security məsələsi və s. ). Gələcək yazılarımızda , workerlərdən daha geniş məlumat verəcəm həmçinin , onların üstünlüklərindən və dezavantajlarından daha ətraflı danışacam

10
Subscribe to my newsletter

Read articles from Zaur Hamidov directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Zaur Hamidov
Zaur Hamidov

Senior Full-Stack Developer ( MEVN )