Siklus proses pekerja layanan adalah bagian yang paling rumit. Jika Anda tidak tahu apa yang ingin dilakukannya dan apa manfaatnya, Anda mungkin merasa seperti sedang melawannya. Namun, setelah mengetahui cara kerjanya, Anda dapat memberikan update yang lancar dan tidak mengganggu kepada pengguna, dengan menggabungkan pola web dan native terbaik.
Ini adalah pembahasan mendalam, tetapi poin-poin di awal setiap bagian mencakup sebagian besar hal yang perlu Anda ketahui.
Intent
Tujuan siklus proses adalah untuk:
- Buat offline-first menjadi mungkin.
- Memungkinkan pekerja layanan baru bersiap tanpa mengganggu pekerja layanan saat ini.
- Pastikan halaman dalam cakupan dikontrol oleh pekerja layanan yang sama (atau tidak ada pekerja layanan) di seluruh halaman.
- Pastikan hanya ada satu versi situs Anda yang berjalan sekaligus.
Yang terakhir cukup penting. Tanpa pekerja layanan, pengguna dapat memuat satu tab ke situs Anda, lalu membuka tab lain di lain waktu. Hal ini dapat menyebabkan dua versi situs Anda berjalan secara bersamaan. Terkadang hal ini tidak masalah, tetapi jika Anda menangani penyimpanan, Anda dapat dengan mudah mendapatkan dua tab yang memiliki pendapat yang sangat berbeda tentang cara penyimpanan bersama mereka harus dikelola. Hal ini dapat menyebabkan error, atau lebih buruk lagi, kehilangan data.
Pekerja layanan pertama
Singkatnya:
- Peristiwa
install
adalah peristiwa pertama yang diperoleh pekerja layanan, dan hanya terjadi sekali. - Promise yang diteruskan ke
installEvent.waitUntil()
menandakan durasi dan keberhasilan atau kegagalan penginstalan Anda. - Pekerja layanan tidak akan menerima peristiwa seperti
fetch
danpush
hingga berhasil menyelesaikan penginstalan dan menjadi "aktif". - Secara default, pengambilan halaman tidak akan melalui pekerja layanan kecuali jika permintaan halaman itu sendiri melalui pekerja layanan. Jadi, Anda harus memuat ulang halaman untuk melihat efek pekerja layanan.
clients.claim()
dapat mengganti default ini, dan mengambil kendali atas halaman yang tidak dikontrol.
Ambil HTML ini:
<!DOCTYPE html>
An image will appear here in 3 seconds:
<script>
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered!', reg))
.catch(err => console.log('Boo!', err));
setTimeout(() => {
const img = new Image();
img.src = '/dog.svg';
document.body.appendChild(img);
}, 3000);
</script>
Kode ini mendaftarkan pekerja layanan, dan menambahkan gambar setelah 3 detik.
Berikut adalah pekerja layanannya, sw.js
:
self.addEventListener('install', event => {
console.log('V1 installing…');
// cache a cat SVG
event.waitUntil(
caches.open('static-v1').then(cache => cache.add('/cat.svg'))
);
});
self.addEventListener('activate', event => {
console.log('V1 now ready to handle fetches!');
});
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// serve the cat SVG from the cache if the request is
// same-origin and the path is '/dog.svg'
if (url.origin == location.origin && url.pathname == '/dog.svg') {
event.respondWith(caches.match('/cat.svg'));
}
});
Fungsi ini meng-cache gambar kucing, dan menayangkannya setiap kali ada permintaan untuk /dog.svg
. Namun, jika menjalankan contoh di atas, Anda akan melihat saat pertama kali memuat halaman. Tekan muat ulang, dan Anda akan melihat kucing.
Cakupan dan kontrol
Cakupan default pendaftaran pekerja layanan adalah ./
relatif terhadap URL skrip. Artinya, jika Anda mendaftarkan pekerja layanan di //example.com/foo/bar.js
, pekerja layanan tersebut memiliki cakupan default //example.com/foo/
.
Kami menyebut halaman, pekerja, dan pekerja bersama clients
. Pekerja layanan Anda hanya dapat mengontrol klien yang termasuk dalam cakupan. Setelah klien "dikontrol", pengambilannya akan melalui pekerja layanan dalam cakupan. Anda dapat mendeteksi apakah klien dikontrol melalui navigator.serviceWorker.controller
yang akan berupa null atau instance pekerja layanan.
Mendownload, mengurai, dan menjalankan
Pekerja layanan pertama Anda akan didownload saat Anda memanggil .register()
. Jika skrip Anda gagal mendownload, mengurai, atau menampilkan error dalam eksekusi awalnya, promise pendaftaran akan ditolak, dan pekerja layanan akan dihapus.
DevTools Chrome menampilkan error di konsol, dan di bagian pekerja layanan pada tab aplikasi:

Instal
Peristiwa pertama yang diperoleh pekerja layanan adalah install
. Fungsi ini dipicu segera setelah pekerja dieksekusi, dan hanya dipanggil sekali per pekerja layanan. Jika Anda mengubah skrip pekerja layanan, browser akan menganggapnya sebagai pekerja layanan yang berbeda, dan akan mendapatkan peristiwa install
-nya sendiri. Saya akan membahas update secara mendetail nanti.
Peristiwa install
adalah kesempatan Anda untuk meng-cache semua yang Anda butuhkan sebelum dapat mengontrol klien. Promise yang Anda teruskan ke event.waitUntil()
memungkinkan browser mengetahui kapan penginstalan selesai, dan apakah berhasil.
Jika promise Anda ditolak, hal ini menandakan bahwa penginstalan gagal, dan browser akan menghapus pekerja layanan. API ini tidak akan pernah mengontrol klien. Artinya, kita dapat mengandalkan cat.svg
yang ada di cache dalam peristiwa fetch
. Ini adalah dependensi.
Aktifkan
Setelah pekerja layanan siap mengontrol klien dan menangani peristiwa fungsional seperti push
dan sync
, Anda akan mendapatkan peristiwa activate
. Namun, hal ini tidak berarti bahwa halaman yang memanggil .register()
akan dikontrol.
Saat pertama kali memuat demo, meskipun dog.svg
diminta lama setelah pekerja layanan diaktifkan, dog.svg
tidak akan menangani permintaan, dan Anda masih melihat gambar. Defaultnya adalah konsistensi, jika halaman Anda dimuat tanpa pekerja layanan, sub-resource-nya juga tidak akan dimuat. Jika Anda memuat demo untuk kedua kalinya (dengan kata lain, memuat ulang halaman), demo tersebut akan dikontrol. Halaman dan gambar akan melalui peristiwa fetch
, dan Anda akan melihat kucing.
clients.claim
Anda dapat mengontrol klien yang tidak terkontrol dengan memanggil clients.claim()
dalam pekerja layanan setelah diaktifkan.
Berikut adalah variasi demo di atas yang memanggil clients.claim()
dalam peristiwa activate
-nya. Anda akan melihat kucing untuk pertama kalinya. Saya mengatakan "harus", karena ini sensitif terhadap waktu. Anda hanya akan melihat kucing jika pekerja layanan diaktifkan dan clients.claim()
diterapkan sebelum gambar mencoba dimuat.
Jika Anda menggunakan pekerja layanan untuk memuat halaman dengan cara yang berbeda dari cara halaman dimuat melalui jaringan, clients.claim()
dapat menjadi masalah, karena pekerja layanan Anda akhirnya mengontrol beberapa klien yang dimuat tanpanya.
Memperbarui pekerja layanan
Singkatnya:
- Pembaruan dipicu jika salah satu hal berikut terjadi:
- Navigasi ke halaman dalam cakupan.
- Peristiwa fungsional seperti
push
dansync
, kecuali jika telah ada pemeriksaan update dalam 24 jam sebelumnya. - Memanggil
.register()
hanya jika URL pekerja layanan telah berubah. Namun, Anda harus menghindari perubahan URL pekerja.
- Sebagian besar browser, termasuk Chrome 68 dan yang lebih baru, secara default mengabaikan header penyimpanan dalam cache saat memeriksa update skrip pekerja layanan terdaftar. Peristiwa ini tetap mematuhi header penyimpanan dalam cache saat mengambil resource yang dimuat di dalam pekerja layanan melalui
importScripts()
. Anda dapat mengganti perilaku default ini dengan menetapkan opsiupdateViaCache
saat mendaftarkan pekerja layanan. - Pekerja layanan Anda dianggap telah diupdate jika byte-nya berbeda dengan yang sudah dimiliki browser. (Kami juga memperluasnya untuk menyertakan skrip/modul yang diimpor.)
- Pekerja layanan yang diperbarui diluncurkan bersama pekerja layanan yang ada, dan mendapatkan peristiwa
install
-nya sendiri. - Jika pekerja baru Anda memiliki kode status non-ok (misalnya, 404), gagal mengurai, menampilkan error selama eksekusi, atau ditolak selama penginstalan, pekerja baru akan dihapus, tetapi pekerja saat ini tetap aktif.
- Setelah berhasil diinstal, pekerja yang diupdate akan
wait
hingga pekerja yang ada tidak mengontrol klien. (Perhatikan bahwa klien tumpang-tindih selama refresh.) self.skipWaiting()
mencegah penantian, yang berarti pekerja layanan akan diaktifkan segera setelah selesai diinstal.
Misalnya, kita mengubah skrip pekerja layanan untuk merespons dengan gambar kuda, bukan kucing:
const expectedCaches = ['static-v2'];
self.addEventListener('install', event => {
console.log('V2 installing…');
// cache a horse SVG into a new cache, static-v2
event.waitUntil(
caches.open('static-v2').then(cache => cache.add('/horse.svg'))
);
});
self.addEventListener('activate', event => {
// delete any caches that aren't in expectedCaches
// which will get rid of static-v1
event.waitUntil(
caches.keys().then(keys => Promise.all(
keys.map(key => {
if (!expectedCaches.includes(key)) {
return caches.delete(key);
}
})
)).then(() => {
console.log('V2 now ready to handle fetches!');
})
);
});
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
// serve the horse SVG from the cache if the request is
// same-origin and the path is '/dog.svg'
if (url.origin == location.origin && url.pathname == '/dog.svg') {
event.respondWith(caches.match('/horse.svg'));
}
});
Lihat demo di atas. Anda akan tetap melihat gambar kucing. Berikut alasannya…
Instal
Perhatikan bahwa saya telah mengubah nama cache dari static-v1
menjadi static-v2
. Artinya, saya dapat menyiapkan cache baru tanpa menimpa item di cache saat ini, yang masih digunakan oleh pekerja layanan lama.
Pola ini membuat cache khusus versi, mirip dengan aset yang akan dipaketkan aplikasi native dengan file yang dapat dieksekusi. Anda mungkin juga memiliki cache yang tidak spesifik per versi, seperti avatars
.
Menunggu
Setelah berhasil diinstal, pekerja layanan yang diupdate akan menunda aktivasi hingga pekerja layanan yang ada tidak lagi mengontrol klien. Status ini disebut "menunggu", dan ini adalah cara browser memastikan bahwa hanya satu versi pekerja layanan Anda yang berjalan dalam satu waktu.
Jika menjalankan demo yang diperbarui, Anda masih akan melihat gambar kucing, karena pekerja V2 belum diaktifkan. Anda dapat melihat pekerja layanan baru yang menunggu di tab "Aplikasi" DevTools:

Meskipun Anda hanya membuka satu tab untuk demo, memuat ulang halaman tidak cukup untuk mengaktifkan versi baru. Hal ini disebabkan oleh cara kerja navigasi browser. Saat Anda menavigasi, halaman saat ini tidak akan hilang hingga header respons diterima, dan meskipun demikian, halaman saat ini dapat tetap ada jika respons memiliki header Content-Disposition
. Karena tumpang-tindih ini, pekerja layanan saat ini selalu mengontrol klien selama refresh.
Untuk mendapatkan update, tutup atau keluar dari semua tab yang menggunakan pekerja layanan saat ini. Kemudian, saat membuka kembali demo, Anda akan melihat kuda.
Pola ini mirip dengan cara Chrome diupdate. Update untuk Chrome didownload di latar belakang, tetapi tidak diterapkan hingga Chrome dimulai ulang. Sementara itu, Anda dapat terus menggunakan versi saat ini tanpa gangguan. Namun, hal ini merepotkan selama pengembangan, tetapi DevTools memiliki cara untuk mempermudahnya, yang akan saya bahas nanti dalam artikel ini.
Aktifkan
Ini diaktifkan setelah pekerja layanan lama dihapus, dan pekerja layanan baru Anda dapat mengontrol klien. Ini adalah waktu yang ideal untuk melakukan hal-hal yang tidak dapat Anda lakukan saat pekerja lama masih digunakan, seperti memigrasikan database dan menghapus cache.
Dalam demo di atas, saya mempertahankan daftar cache yang saya harapkan ada di sana, dan dalam peristiwa activate
, saya menghapus cache lainnya, yang menghapus cache static-v1
lama.
Jika Anda meneruskan promise ke event.waitUntil()
, promise tersebut akan melakukan buffering pada peristiwa fungsional (fetch
, push
, sync
, dll.) hingga promise diselesaikan. Jadi, saat peristiwa fetch
diaktifkan, aktivasi akan selesai sepenuhnya.
Melewati fase tunggu
Fase tunggu berarti Anda hanya menjalankan satu versi situs sekaligus, tetapi jika tidak memerlukan fitur tersebut, Anda dapat mengaktifkan pekerja layanan baru lebih cepat dengan memanggil self.skipWaiting()
.
Hal ini menyebabkan pekerja layanan Anda mengeluarkan pekerja aktif saat ini dan mengaktifkannya sendiri segera setelah memasuki fase menunggu (atau segera jika sudah berada dalam fase menunggu). Hal ini tidak menyebabkan pekerja Anda melewati penginstalan, hanya menunggu.
Tidak masalah kapan Anda memanggil skipWaiting()
, selama selama atau sebelum menunggu. Sangat umum untuk memanggilnya dalam peristiwa install
:
self.addEventListener('install', event => {
self.skipWaiting();
event.waitUntil(
// caching etc
);
});
Namun, Anda mungkin ingin memanggilnya sebagai hasil dari postMessage()
ke pekerja layanan. Artinya, Anda ingin skipWaiting()
setelah interaksi pengguna.
Berikut adalah demo yang menggunakan skipWaiting()
. Anda akan melihat gambar sapi tanpa harus keluar dari halaman. Seperti clients.claim()
, ini adalah perlombaan, sehingga Anda hanya akan melihat sapi jika pekerja layanan baru mengambil, menginstal, dan mengaktifkan sebelum halaman mencoba memuat gambar.
Pembaruan manual
Seperti yang saya sebutkan sebelumnya, browser memeriksa update secara otomatis setelah navigasi dan peristiwa fungsional, tetapi Anda juga dapat memicunya secara manual:
navigator.serviceWorker.register('/sw.js').then(reg => {
// sometime later…
reg.update();
});
Jika Anda memperkirakan pengguna akan menggunakan situs Anda dalam waktu lama tanpa memuat ulang, sebaiknya panggil update()
pada interval (seperti setiap jam).
Hindari mengubah URL skrip pekerja layanan Anda
Jika telah membaca postingan saya tentang praktik terbaik penyimpanan dalam cache, Anda dapat mempertimbangkan untuk memberikan URL unik ke setiap versi pekerja layanan. Jangan lakukan ini! Hal ini biasanya merupakan praktik yang buruk untuk pekerja layanan. Cukup perbarui skrip di lokasinya saat ini.
Hal ini dapat menyebabkan masalah seperti ini:
index.html
mendaftarkansw-v1.js
sebagai pekerja layanan.sw-v1.js
menyimpan dalam cache dan menayangkanindex.html
sehingga berfungsi secara offline-first.- Anda mengupdate
index.html
agar dapat mendaftarkansw-v2.js
baru dan keren.
Jika Anda melakukan hal di atas, pengguna tidak akan pernah mendapatkan sw-v2.js
, karena sw-v1.js
menayangkan index.html
versi lama dari cache-nya. Anda telah menempatkan diri Anda dalam posisi yang mengharuskan Anda mengupdate pekerja layanan untuk mengupdate pekerja layanan. Iiihh.
Namun, untuk demo di atas, saya telah mengubah URL pekerja layanan. Hal ini dilakukan agar Anda dapat beralih antar-versi untuk demo. Ini bukan sesuatu yang akan saya lakukan dalam produksi.
Mempermudah pengembangan
Siklus proses pekerja layanan dibuat dengan mempertimbangkan pengguna, tetapi selama pengembangan, hal ini sedikit merepotkan. Untungnya, ada beberapa alat yang dapat membantu:
Memperbarui saat memuat ulang
Ini adalah favorit saya.

Hal ini mengubah siklus proses menjadi lebih mudah bagi developer. Setiap navigasi akan:
- Ambil ulang pekerja layanan.
- Instal sebagai versi baru meskipun byte-nya identik, yang berarti peristiwa
install
Anda berjalan dan cache Anda diperbarui. - Lewati fase menunggu agar pekerja layanan baru diaktifkan.
- Jelajahi halaman.
Artinya, Anda akan mendapatkan pembaruan di setiap navigasi (termasuk refresh) tanpa harus memuat ulang dua kali atau menutup tab.
Lewati waktu tunggu

Jika ada pekerja yang menunggu, Anda dapat menekan "skip waiting" di DevTools untuk segera mempromosikannya ke "active".
Shift-muat ulang
Jika Anda memuat ulang halaman secara paksa (shift-reload), tindakan ini akan mengabaikan pekerja layanan sepenuhnya. Proses ini tidak akan terkontrol. Fitur ini ada dalam spesifikasi, sehingga berfungsi di browser lain yang mendukung pekerja layanan.
Menangani update
Pekerja layanan dirancang sebagai bagian dari web yang dapat diperluas. Intinya, kami, sebagai developer browser, mengakui bahwa kami tidak lebih baik dalam pengembangan web daripada developer web. Oleh karena itu, kita tidak boleh menyediakan API tingkat tinggi yang sempit yang memecahkan masalah tertentu menggunakan pola yang kita sukai, dan sebagai gantinya memberi Anda akses ke inti browser dan memungkinkan Anda melakukannya sesuai keinginan, dengan cara yang paling sesuai untuk pengguna Anda.
Jadi, untuk mengaktifkan sebanyak mungkin pola, seluruh siklus pembaruan dapat diamati:
navigator.serviceWorker.register('/sw.js').then(reg => {
reg.installing; // the installing worker, or undefined
reg.waiting; // the waiting worker, or undefined
reg.active; // the active worker, or undefined
reg.addEventListener('updatefound', () => {
// A wild service worker has appeared in reg.installing!
const newWorker = reg.installing;
newWorker.state;
// "installing" - the install event has fired, but not yet complete
// "installed" - install complete
// "activating" - the activate event has fired, but not yet complete
// "activated" - fully active
// "redundant" - discarded. Either failed install, or it's been
// replaced by a newer version
newWorker.addEventListener('statechange', () => {
// newWorker.state has changed
});
});
});
navigator.serviceWorker.addEventListener('controllerchange', () => {
// This fires when the service worker controlling this page
// changes, eg a new worker has skipped waiting and become
// the new active worker.
});
Siklus proses terus berlanjut
Seperti yang dapat Anda lihat, memahami siklus proses pekerja layanan akan sangat bermanfaat—dan dengan pemahaman tersebut, perilaku pekerja layanan akan tampak lebih logis, dan tidak terlalu misterius. Pengetahuan tersebut akan membuat Anda lebih percaya diri saat men-deploy dan mengupdate pekerja layanan.