Topik wawancara representatif

Wawancara Frontend: mengoordinasikan Workers dengan Atomics.waitAsync dan pembatalan

FrontendSulit
Tim Redaksi Offer.ccDipublikasikan Diperbarui

Pertanyaan

Beberapa Web Worker harus memproses data live sementara thread utama tetap responsif. Gunakan Atomics.waitAsync untuk merancang protokol wait-and-notify, lalu jelaskan batas waktu, pembatalan, Worker crash, dan browser tanpa dukungan shared-memory.

Petunjuk dan konteks

Pertanyaan ini menguji konkurensi browser, memori bersama (shared memory), dan kontrol siklus hidup (lifecycle). Atomics.waitAsync() mengembalikan sebuah Promise selama lokasi integer bersama masih memiliki nilai yang diharapkan, sehingga pemanggil dapat terus menangani pekerjaan UI. Fungsi ini hanya menerima tampilan Int32Array atau BigInt64Array yang didukung oleh SharedArrayBuffer. Jawaban yang kuat menghubungkan protokol tunggu dengan kepemilikan (ownership), pembatalan, dan fallback yang dapat digunakan.

Apa yang dievaluasi oleh pewawancara

  • Apakah Anda membedakan penantian non-blocking pada thread utama dari Atomics.wait() yang berpotensi memblokir di dalam Worker.
  • Apakah nilai status, waktu notifikasi, timeout, dan notifikasi duplikat memiliki invarian yang eksplisit.
  • Apakah pembatalan, penyembunyian halaman (page hiding), Worker crash, dan pelepasan buffer aman dilakukan.
  • Apakah Anda memahami isolasi lintas-asal (cross-origin isolation), deteksi kapabilitas, dan fallback Transferable.

Pertanyaan klarifikasi

Pastikan apakah data boleh diabaikan/dibuang (dropped), target latensi, jumlah produsen dan konsumen, serta apakah memori harus dibagikan antar-Worker. Tanyakan tentang matriks dukungan browser dan apakah halaman dapat menggunakan konteks aman (secure context) serta isolasi lintas-asal. Periksa apakah skrip pihak ketiga, iframe, atau popup login terpengaruh oleh COOP/COEP. Tentukan apakah pembatalan berarti penghentian oleh pengguna, timeout, atau pelepasan halaman (page teardown).

Kerangka jawaban 30 detik

Saya akan membagi area bersama menjadi kata kontrol (control words) dan slot data, menggunakan status berversi seperti 0=waiting, 1=ready, 2=cancelled, dan 3=closed. Konsumen membaca status secara atomik dan memanggil Atomics.waitAsync hanya saat sedang menunggu. Produsen menulis data, memublikasikan status dengan Atomics.store, dan memanggil Atomics.notify. Promise tersebut hanyalah hasil membangunkan (wake-up); konsumen harus membaca ulang status sebelum memproses. Timeout dan pembatalan adalah transisi status, dan buffer hanya dilepaskan setelah setiap Worker keluar. Browser yang tidak didukung menggunakan chunk Transferable dengan semantik pembatalan dan penanganan kesalahan yang sama.

Solusi langkah demi langkah

1. Tentukan status bersama dan kepemilikan

Area kontrol harus mencakup versi protokol, status, urutan (sequence), panjang (length), dan flag penutupan (closed flag). Produsen hanya menulis ke slot miliknya dan memublikasikan kesiapan setelah penulisan selesai. Konsumen membaca status dan urutan sebelum memproses; konsumen tidak boleh memperlakukan satu notifikasi sebagai satu pesan yang pasti bertahan. Setiap proses bangun (wake-up) harus memeriksa ulang status karena notifikasi dapat bergabung, mengalami race condition, atau merujuk ke batch sebelumnya.

2. Gunakan waitAsync dan notify dengan benar

Atomics.waitAsync(view, index, expected, timeout) pertama-tama membandingkan lokasi memori. Nilai yang berbeda akan mengembalikan not-equal; batas waktu yang terlampaui mengembalikan timed-out; jika tidak, fungsi ini mengembalikan Promise yang dapat di-await. Setelah mengubah status, produsen memanggil Atomics.notify(view, index, count). Protokol ini dapat direpresentasikan sebagai:

js
const control = new Int32Array(new SharedArrayBuffer(16));
const WAITING = 0;
const READY = 1;
const CANCELLED = 2;

async function waitForData(timeout = 1000) {
  const result = Atomics.waitAsync(control, 0, WAITING, timeout);
  const outcome = result.async ? await result.value : result.value;
  const state = Atomics.load(control, 0);
  return { outcome, state };
}

function publishData() {
  Atomics.store(control, 0, READY);
  Atomics.notify(control, 0, 1);
}

3. Tambahkan status pembatalan, batas waktu, dan penutupan

Jangan mencoba menginterupsi Promise yang sudah ada. Tulis status pembatalan dan beri tahu thread yang menunggu sebagai gantinya. Setelah menerima ok, timed-out, atau not-equal, pihak yang menunggu membaca ulang status dan memilih untuk melanjutkan, mencoba lagi, atau keluar. Penutupan pertama-tama menghentikan produksi, kemudian menandai area sebagai tertutup dan memberi tahu setiap pihak yang menunggu; buffer hanya boleh dilepaskan setelah keluarnya Worker dikonfirmasi.

4. Tangani race condition dan backpressure

Banyak produsen memerlukan CAS atau allocator untuk mengklaim slot; mereka tidak boleh menulis ke urutan yang sama secara bersamaan. Ketika antrean penuh, pilih antara dropping, penimpaan (overwriting), atau backpressure sesuai dengan kontrak produk, lalu catat kedalaman dan jumlah data yang dibuang. Konsumen harus menguras semua nomor urut yang dipublikasikan dalam sebuah perulangan alih-alih mengartikan satu kali wake-up sebagai satu pesan.

5. Kelola crash dan siklus hidup halaman

Thread utama memiliki state machine dan detak jantung (heartbeat) Worker. Pada error atau tenggat waktu, hentikan penulisan baru, tandai tugas sebagai gagal, dan minta Worker yang tersisa untuk keluar. Saat halaman disembunyikan atau komponen dilepas (unmount), kirimkan pembatalan, tunggu selama periode waktu tertentu, lalu hentikan (terminate). Mulai ulang (restart) akan membuat versi area kontrol baru alih-alih menggunakan kembali nilai urutan dan status yang sudah usang.

6. Deteksi kapabilitas dan degradasi secara aman

Saat startup, periksa crossOriginIsolated, SharedArrayBuffer, Atomics.waitAsync, dan dukungan Worker. Isolasi lintas-asal mengubah perilaku sumber daya pihak ketiga dan popup, sehingga performa bukanlah alasan untuk melemahkan kebijakan keamanan. Tanpa kapabilitas yang diperlukan, gunakan chunk ArrayBuffer yang Transferable atau postMessage biasa, dengan tetap mempertahankan field pembatalan, timeout, progres, dan error. Ukur pembagian jalur (path share) dan tail latency.

Contoh jawaban berkualitas tinggi

Saya akan mendeteksi kapabilitas dan memverifikasi persyaratan konteks aman serta isolasi lintas-asal sebelum membuat area kontrol berversi. Produsen memublikasikan status secara atomik setelah menulis data dan memberi tahu pihak yang menunggu. Konsumen memanggil Atomics.waitAsync hanya saat status sedang menunggu, lalu membaca ulang status dan urutan terlepas dari hasil Promise. Pembatalan, timeout, dan penutupan adalah status yang eksplisit. Proses shutdown menghentikan produksi, membangunkan pihak yang menunggu, mengonfirmasi keluarnya Worker, dan baru kemudian melepaskan memori bersama. Kepemilikan dan nomor urut mencegah konsumsi duplikat, sementara perilaku antrean penuh merupakan keputusan produk yang terukur. Jika memori bersama atau API tidak tersedia, chunk Transferable menyediakan siklus hidup dan kontrak kesalahan yang sama.

Kesalahan umum

  • Mengasumsikan notify menjamin satu wake-up per pesan.
  • Memanggil wait yang memblokir pada thread utama sehingga membekukan UI.
  • Mempercayai hasil Promise tanpa membaca ulang status bersama dan urutannya.
  • Langsung melepaskan buffer saat pembatalan sementara Worker masih berjalan.
  • Mengabaikan batasan isolasi lintas-asal dan sumber daya pihak ketiga.
  • Beralih ke postMessage tetapi menghilangkan semantik batas waktu, pembatalan, atau kesalahan.

Pertanyaan lanjutan

Kapan Anda akan memilih waitAsync daripada wait?

waitAsync mengembalikan Promise tanpa memblokir thread pemanggil, yang cocok untuk thread utama dan kode berbasis event. wait dapat memblokir Worker, tetapi hanya jika waktu pemblokiran Worker tersebut tidak merugikan UI atau pekerjaan penting lainnya. Keduanya memerlukan pemeriksaan status, timeout, dan protokol penutupan.

Mengapa harus membaca ulang status setelah notifikasi?

Notifikasi hanya menandakan bahwa suatu kondisi mungkin telah berubah. Notifikasi dapat bergabung, membangunkan konsumen pesaing lainnya, atau sesuai dengan batch sebelumnya. Membaca status, urutan, dan panjang data memastikan apakah data benar-benar tersedia.

Bagaimana cara mencegah pekerjaan setelah pembatalan?

Publikasikan pembatalan dalam status bersama dan periksa saat mengklaim slot, sebelum memproses, dan sebelum mengirimkan hasil. Sertakan versi tugas dalam pesan hasil; thread utama akan menolak hasil dari versi yang telah dibatalkan.

Kapan memori bersama harus ditinggalkan?

Gunakan chunk Transferable ketika header isolasi merusak dependensi kritis, cakupan browser tidak memadai, biaya debugging melebihi keuntungan, atau throughput yang dibutuhkan moderat. Biarkan kapabilitas dan telemetri yang memilih jalurnya alih-alih berasumsi bahwa memori bersama selalu merupakan optimasi yang tepat.

Sumber publik

Pertanyaan terkait