Pekerja web beraksi: mengapa mereka membantu, dan bagaimana Anda harus menggunakannya

Javascript adalah utas tunggal dan beberapa skrip tidak dapat dijalankan pada saat yang bersamaan. Jadi jika kita menjalankan tugas komputasi yang berat, terkadang halaman kita menjadi tidak responsif dan pengguna tidak dapat melakukan apa pun sampai eksekusi tersebut selesai.

Sebagai contoh:

average = (numbers) => { let startTime = new Date().getTime(); let len = numbers, sum = 0, i; if (len === 0) { return 0; } for (i = 0; i  { alert("Hello World !!"); } /* Paste the above code in browser dev tool console and try to call average(10000) and hello one by one */

Dalam contoh di atas, jika Anda memanggil rata-rata sebelum metode hello , maka halaman Anda akan menjadi tidak responsif dan Anda tidak akan dapat mengklik Hello sampai eksekusi rata - rata selesai.

Anda dapat melihat bahwa ketika rata - rata dipanggil dengan 10000 sebagai input pertama, butuh ~ 1,82 detik. Selama jangka waktu tersebut halaman menjadi tidak responsif dan Anda tidak dapat mengklik tombol halo.

Pemrograman Asinkron

Javascript memberikan cara bagi pengembang untuk menulis kode asinkron . Dengan menulis kode asinkron, Anda dapat menghindari masalah semacam ini dalam aplikasi Anda karena memungkinkan UI aplikasi Anda menjadi responsif, dengan "menjadwalkan" bagian kode untuk dieksekusi nanti di loop peristiwa.

Contoh yang baik dari pemrograman asinkron adalah XHR request, dalam hal ini kita menekan API secara asinkron dan sambil menunggu respon, kode lain dapat dieksekusi. Tetapi ini terbatas pada kasus penggunaan tertentu yang sebagian besar terkait dengan API web.

Cara lain untuk menulis kode asinkron adalah dengan menggunakan setTimeoutmetode. Dalam beberapa kasus, Anda bisa mencapai hasil yang baik dalam membebaskan UI dari komputasi yang berjalan lebih lama dengan menggunakan setTimeout. Misalnya, dengan menggabungkan komputasi kompleks dalam setTimeoutpanggilan terpisah .

Sebagai contoh:

average = (numbers) => { let startTime = new Date().getTime(); var len = numbers, sum = 0, i; if (len === 0) { return 0; } let calculateSumAsync = (i) => { if (i  { sum += i; calculateSumAsync(i + 1); }, 0); } else { // The end of the array is reached so we're invoking the alert. let endTime = new Date().getTime(); alert('Average - ', sum / len); } }; calculateSumAsync(0); }; hello = () => { alert('Hello World !!') };

Dalam contoh ini, Anda dapat melihat bahwa setelah Anda mengklik tombol Hitung Rata-Rata , Anda masih dapat mengklik tombol Halo (yang kemudian akan menampilkan pesan peringatan). Cara pemrograman seperti ini tentu saja tidak memblokir tetapi membutuhkan terlalu banyak waktu, dan tidak layak untuk aplikasi dunia nyata.

Di sini, untuk masukan yang sama 10.000, butuh ~ 60 detik, yang sangat tidak efisien.

Jadi, bagaimana kita menyelesaikan masalah semacam ini secara efisien?

Jawabannya adalah Pekerja Web.

Apa itu pekerja web?

Pekerja web di Javascript adalah cara yang bagus untuk menjalankan beberapa tugas yang sangat melelahkan dan waktu mengambil utas terpisah dari utas utama. Mereka berjalan di latar belakang dan melakukan tugas tanpa mengganggu antarmuka pengguna.

Pekerja Web bukan bagian dari JavaScript, mereka adalah fitur browser yang dapat diakses melalui JavaScript.

Pekerja web dibuat oleh fungsi konstruktor Worker () yang menjalankan file JS bernama.

// create a dedicated web worker const myWorker = new Worker('worker.js');

Jika file yang ditentukan ada maka itu akan diunduh secara asinkron dan jika tidak maka pekerja akan gagal secara diam-diam, sehingga aplikasi Anda akan tetap berfungsi dalam kasus 404.

Kita akan mempelajari lebih lanjut tentang pembuatan dan cara kerja pekerja web di bagian selanjutnya.

Utas pekerja memiliki konteksnya sendiri dan oleh karena itu Anda hanya dapat mengakses fitur yang dipilih di dalam utas pekerja seperti - soket web, DB yang diindeks.

Ada beberapa batasan dengan pekerja web -

  1. Anda tidak bisa secara langsung memanipulasi DOM dari dalam pekerja.
  2. Anda tidak dapat menggunakan beberapa metode dan properti default dari objek jendela karena objek jendela tidak tersedia di dalam utas pekerja.
  3. Konteks di dalam utas pekerja dapat diakses melalui DedicatedWorkerGlobalScope atau SharedWorkerGlobalScope bergantung pada penggunaan.

Fitur Pekerja Web

Ada dua jenis pekerja web -

  1. Pekerja web khusus - Pekerja khusus hanya dapat diakses oleh skrip yang memanggilnya.
  2. Pekerja web bersama - Pekerja bersama dapat diakses dengan beberapa skrip - meskipun mereka sedang diakses oleh jendela yang berbeda, iframe, atau bahkan pekerja.

Mari kita bahas lebih lanjut tentang kedua jenis pekerja web tersebut -

Penciptaan pekerja web

Kreasi kurang lebih sama untuk pekerja web Berdedikasi dan Bersama.

Pekerja web yang berdedikasi

  • Membuat pekerja baru itu sederhana, cukup panggil konstruktor Pekerja dan teruskan jalur skrip yang ingin Anda jalankan sebagai pekerja.
// create a dedicated web worker const myWorker = new Worker('worker.js');

Pekerja web bersama:

  • Membuat pekerja bersama baru hampir sama dengan pekerja khusus, tetapi dengan nama konstruktor yang berbeda.
// creating a shared web worker const mySharedWorker = new SharedWorker('worker.js');

Komunikasi antara utas utama dan pekerja

Komunikasi antara thread utama dan thread pekerja terjadi melalui postMessage metode dan onmessage event handler.

Pekerja web yang berdedikasi

Untuk web worker yang berdedikasi, sistem komunikasinya sederhana. Anda hanya perlu menggunakan metode postMessage kapan pun Anda ingin mengirim pesan ke pekerja.

(() => { // new worker let myWorker = new Worker('worker.js'); // event handler to recieve message from worker myWorker.onmessage = (e) => { document.getElementById('time').innerHTML = `${e.data.time} seconds`; }; let average = (numbers) => { // sending message to web worker with an argument myWorker.postMessage(numbers); } average(1000); })();

Dan di dalam pekerja web Anda bisa merespons ketika pesan diterima dengan menulis blok pengendali kejadian seperti ini:

onmessage = (e) => { let numbers = e.data; let startTime = new Date().getTime(); let len = numbers, sum = 0, i; if (len === 0) { return 0; } for (i = 0; i < len; i++) { sum += i; } let endTime = new Date().getTime(); postMessage({average: sum / len, time: ((endTime - startTime) / 1000)}) };

The onmessagehandler memungkinkan untuk menjalankan beberapa kode setiap kali pesan diterima.

Di sini kami menghitung rata-rata angka dan kemudian menggunakannya postMessage()lagi, untuk mengirim hasilnya kembali ke utas utama.

Seperti yang Anda lihat pada baris 6 di main.js, kami telah menggunakan acara onmessage pada instance pekerja. Jadi, setiap kali thread pekerja menggunakan postMessage, onmessage di thread utama akan dipicu.

  • Pekerja web bersama

    Dalam kasus pekerja web bersama, sistem komunikasi sedikit berbeda. Karena satu pekerja dibagi di antara beberapa skrip, kita perlu berkomunikasi melalui objek port dari instance pekerja. Ini dilakukan secara implisit dalam kasus pekerja yang berdedikasi. Anda perlu menggunakan metode postMessage kapan pun Anda ingin mengirim pesan ke pekerja.

(() => { // new worker let myWorker = new Worker('worker.js'); // event handler to recieve message from worker myWorker.onmessage = (e) => { document.getElementById('time').innerHTML = `${e.data.time} seconds`; }; let average = (numbers) => { // sending message to web worker with an argument myWorker.postMessage(numbers); } average(1000);

Di dalam pekerja web ( main-shared-worker.js ), ini sedikit rumit. Pertama, kami menggunakan onconnectpenangan untuk mengaktifkan kode ketika koneksi ke port terjadi ( baris 2 ).

Kami menggunakan portsatribut objek acara ini untuk mengambil port dan menyimpannya dalam variabel ( baris 4 ).

Selanjutnya, kami menambahkan messagepenangan pada port untuk melakukan perhitungan dan mengembalikan hasilnya ke utas utama ( baris 7 dan baris 25 ) seperti ini:

onmessage = (e) => { let numbers = e.data; let startTime = new Date().getTime(); let len = numbers, sum = 0, i; if (len === 0) { return 0; } for (i = 0; i < len; i++) { sum += i; } let endTime = new Date().getTime(); postMessage({average: sum / len, time: ((endTime - startTime) / 1000)}) };

Termination of a web worker

If you need to immediately terminate a running worker from the main thread, you can do so by calling the worker’s terminate method:

// terminating a web worker instance myWorker.terminate();

The worker thread is killed immediately without an opportunity to complete its operations.

Spawning of web worker

Workers may spawn more workers if they wish. But they must be hosted within the same origin as the parent page.

Importing Scripts

Worker threads have access to a global function, importScripts(), which lets them import scripts.

importScripts(); /* imports nothing */ importScripts('foo.js'); /* imports just "foo.js" */ importScripts('foo.js', 'bar.js'); /* imports two scripts */ importScripts('//example.com/hello.js'); /* You can import scripts from other origins */

Working Demo

We have discussed some of the approaches above to achieve async programming so that our UI doesn’t get blocked due to any heavy computational task. But there are some limitations to those approaches. So we can use web workers to solve these kind of problems efficiently.

Click here to run this live demo.

Here, you will see 3 sections:

  1. Blocking Code:

    When you click on calculate average, the loader does not display and after some time you see the final result and time taken. This is because as soon as the average method gets called, I have triggered the showLoader method also. But since JS is single threaded, it won’t execute showLoader until the execution of average gets completed. So, you won’t be able to see the loader in this case ever.

  2. Async Code:

    In this I tried to achieve the same functionality by using the setTimeout method and putting every function execution into an event loop. You will see the loader in this case, but the response takes time as compared to the method defined above.

  3. Web worker:

    This is an example of using a web worker. In this you will see the loader as soon as you click on calculate average and you will get a response in the same time as of method 1, for the same number.

You can access the source code for the same here.

Advanced concepts

There are some advanced concepts related to web workers. We won’t be discussing them in detail, but its good to know about them.

  1. Content Security Policy —

    Web workers have their own execution context independent of the document that created them and because of this reason they are not governed by the Content Security Policy of the parent thread/worker.

    The exception to this is if the worker script's origin is a globally unique identifier (for example, if its URL has a scheme of data or blob). In this case, the worker inherit the content security policy of the document or worker that created it.

  2. Transferring data to and from workers

    Data passed between main and worker thread is copied and not shared. Objects are serialized as they're handed to the worker, and subsequently, de-serialized on the other end. The page and worker do not share the same instance, so the end result is that a duplicate is created on each end.

    Browsers implemented Structured Cloning algorithm to achieve this.

  3. Embedded workers —

    You can also embed the code of worker inside a web page (html). For this you need to add a script tag without a src attribute and assign a non-executable MIME type to it, like this:

    embedded worker   // This script WON'T be parsed by JS engines because its MIME type is text/js-worker. var myVar = 'Hello World!'; // worker block function onmessage(e) { // worker code }    

There are a lot of use cases to use web workers in our application. I have just discussed a small scenario. Hope this helps you understand the concept of web workers.

[Links]

Github Repo : //github.com/bhushangoel/webworker-demo-1 Web worker in action : //bhushangoel.github.io/webworker-demo-1/JS demo showcase : //bhushangoel.github.io/

Thank you for reading.

Happy Learning :)

Originally published at www.thehungrybrain.com.