Komunikasi dua arah dengan pekerja layanan

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

Dalam beberapa kasus, aplikasi web mungkin perlu membuat saluran komunikasi dua arah antara halaman dan pekerja layanan.

Misalnya: dalam PWA podcast, seseorang dapat membuat fitur yang memungkinkan pengguna mendownload episode untuk digunakan saat offline dan memungkinkan pekerja layanan terus menginformasikan progres ke halaman secara berkala, sehingga thread utama dapat mengupdate UI.

Dalam panduan ini, kita akan mempelajari berbagai cara untuk menerapkan komunikasi dua arah antara konteks Window dan pekerja layanan, dengan menjelajahi berbagai API, library Workbox, serta beberapa kasus lanjutan.

Diagram yang menunjukkan pekerja layanan dan halaman yang bertukar pesan.

Menggunakan Workbox

workbox-window adalah kumpulan modul library Workbox yang dimaksudkan untuk berjalan dalam konteks jendela. Class Workbox menyediakan metode messageSW() untuk mengirim pesan ke pekerja layanan terdaftar instance dan menunggu respons.

Kode halaman berikut membuat instance Workbox baru dan mengirim pesan ke pekerja layanan untuk mendapatkan versinya:

const wb = new Workbox('/sw.js');
wb.register();

const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);

Pekerja layanan menerapkan pemroses pesan di sisi lain, dan merespons pekerja layanan yang terdaftar:

const SW_VERSION = '1.0.0';

self.addEventListener('message', (event) => {
  if (event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage(SW_VERSION);
  }
});

Pada prinsipnya, library menggunakan API browser yang akan kita tinjau di bagian berikutnya: Message Channel, tetapi memisahkan banyak detail implementasi, sehingga lebih mudah digunakan, sekaligus memanfaatkan dukungan browser luas yang dimiliki API ini.

Diagram yang menunjukkan komunikasi dua arah antara halaman dan pekerja layanan, menggunakan Jendela Workbox.

Menggunakan API Browser

Jika library Workbox tidak memadai untuk kebutuhan Anda, ada beberapa API tingkat lebih rendah yang tersedia untuk menerapkan komunikasi "dua arah" antara halaman dan pekerja layanan. Mereka memiliki beberapa kesamaan dan perbedaan:

Persamaan:

  • Dalam semua kasus, komunikasi dimulai di satu sisi melalui antarmuka postMessage() dan diterima di sisi lainnya dengan menerapkan pengendali message.
  • Dalam praktiknya, semua API yang tersedia memungkinkan kita untuk menerapkan kasus penggunaan yang sama, tetapi beberapa di antaranya mungkin menyederhanakan pengembangan dalam beberapa skenario.

Perbedaan:

  • Ada berbagai cara untuk mengidentifikasi sisi lain komunikasi: beberapa dari mereka menggunakan referensi eksplisit ke konteks lain, sementara yang lain dapat berkomunikasi secara implisit melalui objek proxy yang dibuat instance-nya di setiap sisi.
  • Dukungan browser bervariasi di antaranya.
Diagram yang menunjukkan komunikasi dua arah antara halaman dan pekerja layanan, serta API browser yang tersedia.

API Saluran Siaran

Dukungan Browser

  • 54
  • 79
  • 38
  • 15,4

Sumber

Broadcast Channel API memungkinkan komunikasi dasar antar-konteks penjelajahan melalui objek BroadcastChannel.

Untuk mengimplementasikannya, pertama-tama, setiap konteks harus membuat instance objek BroadcastChannel dengan ID yang sama, lalu mengirim serta menerima pesan darinya:

const broadcast = new BroadcastChannel('channel-123');

Objek BroadcastChannel mengekspos antarmuka postMessage() untuk mengirim pesan ke konteks pemrosesan apa pun:

//send message
broadcast.postMessage({ type: 'MSG_ID', });

Konteks browser apa pun dapat memproses pesan melalui metode onmessage dari objek BroadcastChannel:

//listen to messages
broadcast.onmessage = (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //process message...
  }
};

Seperti yang terlihat, tidak ada referensi eksplisit untuk konteks tertentu, sehingga tidak perlu mendapatkan referensi terlebih dahulu ke pekerja layanan atau klien tertentu.

Diagram yang menunjukkan komunikasi dua arah antara halaman dan pekerja layanan, menggunakan objek Broadcast Channel.

Kekurangannya adalah, pada saat tulisan ini ditulis, API tersebut memiliki dukungan dari Chrome, Firefox, dan Edge, tetapi browser lain, seperti Safari, belum mendukungnya.

Client API

Dukungan Browser

  • 40
  • 17
  • 44
  • 11.1

Sumber

Client API memungkinkan Anda mendapatkan referensi ke semua objek WindowClient yang mewakili tab aktif yang dikontrol pekerja layanan.

Karena halaman dikontrol oleh satu pekerja layanan, halaman akan memproses dan mengirim pesan ke pekerja layanan yang aktif secara langsung melalui antarmuka serviceWorker:

//send message
navigator.serviceWorker.controller.postMessage({
  type: 'MSG_ID',
});

//listen to messages
navigator.serviceWorker.onmessage = (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //process response
  }
};

Demikian pula, pekerja layanan memproses pesan dengan mengimplementasikan pemroses onmessage:

//listen to messages
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //Process message
  }
});

Untuk berkomunikasi kembali dengan salah satu kliennya, pekerja layanan memperoleh array objek WindowClient dengan menjalankan metode seperti Clients.matchAll() dan Clients.get(). Kemudian, metode ini dapat postMessage() salah satunya:

//Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
  if (clients && clients.length) {
    //Respond to last focused tab
    clients[0].postMessage({type: 'MSG_ID'});
  }
});
Diagram yang menunjukkan pekerja layanan yang berkomunikasi dengan array klien.

Client API adalah opsi yang baik untuk berkomunikasi secara mudah dengan semua tab aktif dari pekerja layanan dengan cara yang relatif mudah. API ini didukung oleh semua browser utama, tetapi tidak semua metodenya tersedia. Jadi, pastikan untuk memeriksa dukungan browser sebelum menerapkannya di situs Anda.

Saluran Pesan

Dukungan Browser

  • 2
  • 12
  • 41
  • 5

Sumber

Saluran Pesan mengharuskan Anda menentukan dan meneruskan port dari satu konteks ke konteks lain untuk membuat saluran komunikasi dua arah.

Untuk melakukan inisialisasi pada saluran, halaman akan membuat instance objek MessageChannel dan menggunakannya untuk mengirim port ke pekerja layanan terdaftar. Halaman ini juga mengimplementasikan pemroses onmessage pada halaman tersebut untuk menerima pesan dari konteks lain:

const messageChannel = new MessageChannel();

//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
  messageChannel.port2,
]);

//Listen to messages
messageChannel.port1.onmessage = (event) => {
  // Process message
};
Diagram yang menunjukkan halaman yang meneruskan port ke pekerja layanan untuk menjalin komunikasi dua arah.

Pekerja layanan menerima port, menyimpan referensi ke port tersebut, dan menggunakannya untuk mengirim pesan ke sisi lain:

let communicationPort;

//Save reference to port
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PORT_INITIALIZATION') {
    communicationPort = event.ports[0];
  }
});

//Send messages
communicationPort.postMessage({type: 'MSG_ID'});

MessageChannel saat ini didukung oleh semua browser utama.

API Lanjutan: Sinkronisasi Latar Belakang dan Pengambilan Latar Belakang

Dalam panduan ini, kita telah mempelajari cara-cara mengimplementasikan teknik komunikasi dua arah, untuk kasus yang relatif sederhana, seperti meneruskan pesan string yang menjelaskan operasi yang akan dilakukan, atau daftar URL untuk meng-cache dari satu konteks ke konteks lainnya. Di bagian ini, kita akan mempelajari dua API untuk menangani skenario tertentu: kurangnya konektivitas dan download yang lama.

Sinkronisasi Latar Belakang

Dukungan Browser

  • 49
  • 79
  • x
  • x

Sumber

Aplikasi chat mungkin ingin memastikan bahwa pesan tidak pernah hilang karena konektivitas yang buruk. Background Sync API memungkinkan Anda menunda tindakan untuk dicoba lagi saat pengguna memiliki konektivitas yang stabil. Hal ini berguna untuk memastikan bahwa apa pun yang ingin dikirim pengguna, benar-benar dikirim.

Halaman mendaftarkan sync, bukan antarmuka postMessage():

navigator.serviceWorker.ready.then(function (swRegistration) {
  return swRegistration.sync.register('myFirstSync');
});

Pekerja layanan kemudian memproses peristiwa sync untuk memproses pesan:

self.addEventListener('sync', function (event) {
  if (event.tag == 'myFirstSync') {
    event.waitUntil(doSomeStuff());
  }
});

Fungsi doSomeStuff() akan menampilkan promise yang menunjukkan keberhasilan/kegagalan apa pun yang dicoba lakukan. Jika terpenuhi, sinkronisasi selesai. Jika gagal, sinkronisasi lain akan dijadwalkan untuk dicoba lagi. Mencoba ulang sinkronisasi juga menunggu konektivitas, dan menggunakan back-off eksponensial.

Setelah operasi dijalankan, pekerja layanan kemudian dapat berkomunikasi kembali dengan halaman untuk mengupdate UI, dengan menggunakan salah satu API komunikasi yang dipelajari sebelumnya.

Google Penelusuran menggunakan Sinkronisasi Latar Belakang untuk mempertahankan kueri yang gagal karena konektivitas buruk, dan mencoba lagi nanti saat pengguna online. Setelah operasi dijalankan, mereka akan menyampaikan hasilnya kepada pengguna melalui notifikasi push web:

Diagram yang menunjukkan halaman yang meneruskan port ke pekerja layanan untuk menjalin komunikasi dua arah.

Pengambilan Latar Belakang

Dukungan Browser

  • 74
  • 79
  • x
  • x

Sumber

Untuk pekerjaan yang relatif singkat seperti mengirim pesan, atau daftar URL untuk di-cache, opsi yang telah dipelajari sejauh ini adalah pilihan yang baik. Jika tugas memerlukan waktu terlalu lama, browser akan menghentikan pekerja layanan, jika tidak, hal ini berisiko terhadap privasi dan baterai pengguna.

Dengan Background Fetch API, Anda dapat mengalihkan tugas yang panjang ke pekerja layanan, seperti mendownload film, podcast, atau level game.

Untuk berkomunikasi dengan pekerja layanan dari halaman, gunakan backgroundFetch.fetch, bukan postMessage():

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.fetch(
    'my-fetch',
    ['/ep-5.mp3', 'ep-5-artwork.jpg'],
    {
      title: 'Episode 5: Interesting things.',
      icons: [
        {
          sizes: '300x300',
          src: '/ep-5-icon.png',
          type: 'image/png',
        },
      ],
      downloadTotal: 60 * 1024 * 1024,
    },
  );
});

Objek BackgroundFetchRegistration memungkinkan halaman memproses peristiwa progress untuk mengikuti progres download:

bgFetch.addEventListener('progress', () => {
  // If we didn't provide a total, we can't provide a %.
  if (!bgFetch.downloadTotal) return;

  const percent = Math.round(
    (bgFetch.downloaded / bgFetch.downloadTotal) * 100,
  );
  console.log(`Download progress: ${percent}%`);
});
Diagram yang menunjukkan halaman yang meneruskan port ke pekerja layanan untuk menjalin komunikasi dua arah.
UI diupdate untuk menunjukkan progres download (kiri). Berkat pekerja layanan, operasi dapat terus berjalan ketika semua tab telah ditutup (kanan).

Langkah berikutnya

Dalam panduan ini, kami telah membahas kasus komunikasi paling umum antara pekerja halaman dan pekerja layanan (komunikasi dua arah).

Sering kali, seseorang mungkin hanya memerlukan satu konteks untuk berkomunikasi dengan konteks yang lain, tanpa menerima respons. Lihat panduan berikut untuk mendapatkan panduan cara menerapkan teknik searah di halaman Anda dari dan ke pekerja layanan, beserta kasus penggunaan dan contoh produksi:

  • Panduan penyimpanan cache implisit: Memanggil pekerja layanan dari halaman untuk meng-cache resource terlebih dahulu (mis. dalam skenario pengambilan data).
  • Menyiarkan update: Memanggil halaman dari pekerja layanan untuk menginformasikan update penting (mis., versi webapp baru tersedia).