Topik temu duga representatif

Temu duga Frontend: menyelaraskan Workers dengan Atomics.waitAsync dan pembatalan

FrontendSukar
Pasukan Editorial Offer.ccDiterbitkan Dikemas kini

Soalan

Beberapa Web Workers mesti memproses data langsung sementara urutan utama (main thread) kekal responsif. Gunakan Atomics.waitAsync untuk mereka bentuk protokol tunggu-dan-maklumkan (wait-and-notify), kemudian terangkan tamat masa, pembatalan, kegagalan Worker, dan pelayar tanpa sokongan memori kongsi.

Gesaan dan konteks

Soalan ini menguji konkurensi pelayar, memori kongsi (shared memory), dan kawalan kitaran hayat. Atomics.waitAsync() mengembalikan Promise sementara lokasi integer kongsi masih mempunyai nilai yang dijangkakan, membolehkan pemanggil terus mengendalikan kerja UI. Ia hanya menerima paparan Int32Array atau BigInt64Array yang disandarkan oleh SharedArrayBuffer. Jawapan yang mantap menghubungkan protokol menunggu dengan pemilikan, pembatalan, dan sandaran yang boleh digunakan.

Perkara yang dinilai oleh penemu duga

  • Sama ada anda membezakan penantian tidak menyekat pada urutan utama daripada Atomics.wait() yang berpotensi menyekat di dalam Worker.
  • Sama ada nilai keadaan, masa pemberitahuan, tamat masa, dan pemberitahuan pendua mempunyai invarians yang eksplisit.
  • Sama ada pembatalan, penyembunyian halaman (page hiding), kegagalan Worker, dan pelepasan penimbal (buffer) adalah selamat.
  • Sama ada anda memahami pengasingan silang-asal (cross-origin isolation), pengesanan keupayaan, dan sandaran Transferable.

Soalan penjelasan

Sahkan sama ada data boleh digugurkan, sasaran pendaman (latency), bilangan pengeluar dan pengguna, dan sama ada memori mesti dikongsi merentasi Workers. Tanya tentang matriks sokongan pelayar dan sama ada halaman boleh menggunakan konteks selamat dan pengasingan silang-asal. Periksa sama ada skrip pihak ketiga, iframe, atau tetingkap timbul log masuk dipengaruhi oleh COOP/COEP. Tentukan sama ada pembatalan bermaksud henti pengguna, tamat masa, atau penutupan halaman.

Garis kasar jawapan 30 saat

Saya akan membahagikan kawasan kongsi kepada perkataan kawalan dan slot data, menggunakan keadaan berversi seperti 0=waiting, 1=ready, 2=cancelled, dan 3=closed. Pengguna membaca keadaan secara atomik dan memanggil Atomics.waitAsync hanya semasa ia sedang menunggu. Pengeluar menulis data, menerbitkan keadaan dengan Atomics.store, dan memanggil Atomics.notify. Promise tersebut hanyalah hasil bangun tidur (wake-up); pengguna mesti membaca semula keadaan sebelum memproses. Tamat masa dan pembatalan adalah peralihan keadaan, dan penimbal hanya dilepaskan selepas setiap Worker keluar. Pelayar yang tidak disokong menggunakan ketulan Transferable dengan semantik pembatalan dan ralat yang sama.

Penyelesaian langkah demi langkah

1. Tentukan keadaan kongsi dan pemilikan

Kawasan kawalan harus merangkumi versi protokol, keadaan, urutan, panjang, dan bendera ditutup. Pengeluar hanya menulis slot miliknya dan menerbitkan kesediaan selepas penulisan selesai. Pengguna membaca keadaan dan urutan sebelum memproses; ia tidak boleh menganggap satu pemberitahuan sebagai satu mesej yang kekal. Setiap kali bangun, semak semula keadaan kerana pemberitahuan boleh bergabung, berlumba, atau merujuk kepada kumpulan (batch) terdahulu.

2. Gunakan waitAsync dan notify dengan betul

Atomics.waitAsync(view, index, expected, timeout) terlebih dahulu membandingkan lokasi tersebut. Nilai yang berbeza mengembalikan not-equal; tamat masa mengembalikan timed-out; jika tidak, ia mengembalikan Promise yang boleh ditunggu (awaitable). Selepas menukar keadaan, pengeluar memanggil Atomics.notify(view, index, count). Protokol ini boleh diwakili 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. Tambah keadaan pembatalan, tamat masa, dan penutupan

Jangan cuba mengganggu Promise yang sedia ada. Sebaliknya, tulis keadaan pembatalan dan maklumkan kepada pemanggil yang sedang menunggu. Selepas menerima ok, timed-out, atau not-equal, pihak yang menunggu membaca semula keadaan dan memilih untuk meneruskan, mencuba semula, atau keluar. Penutupan terlebih dahulu menghentikan pengeluaran, kemudian menandakan kawasan itu ditutup dan memaklumkan setiap pihak yang menunggu; hanya selepas keluar Worker disahkan, barulah penimbal dilepaskan.

4. Kendalikan perlumbaan (races) dan tekanan belakang (backpressure)

Pelbagai pengeluar memerlukan CAS atau pengagih (allocator) untuk menuntut slot; mereka tidak boleh menulis urutan yang sama secara serentak. Apabila baris gilir penuh, pilih antara pengguguran, penulisan ganti, atau tekanan belakang mengikut kontrak produk, dan rekod kedalaman serta pengguguran. Pengguna harus mengalirkan semua nombor urutan yang diterbitkan dalam gelung daripada mentafsir satu panggilan bangun sebagai satu mesej.

5. Urus kegagalan dan kitaran hayat halaman

Urutan utama memiliki mesin keadaan Worker dan denyutan jantung (heartbeat). Pada error atau tarikh akhir, hentikan penulisan baharu, tandakan tugas sebagai gagal, dan minta baki Workers keluar. Apabila halaman disembunyikan atau komponen dinyahlekap (unmount), hantar pembatalan, tunggu untuk tempoh terhad, kemudian tamatkan. Mulakan semula mencipta versi kawasan kawalan baharu dan bukannya menggunakan semula urutan dan nilai keadaan yang lapuk.

6. Kesan keupayaan dan turun taraf dengan selamat

Semasa permulaan, periksa crossOriginIsolated, SharedArrayBuffer, Atomics.waitAsync, dan sokongan Worker. Pengasingan silang-asal mengubah tingkah laku sumber pihak ketiga dan tetingkap timbul, jadi prestasi bukanlah sebab untuk melemahkan dasar keselamatan. Tanpa keupayaan yang diperlukan, gunakan ketulan Transferable ArrayBuffer atau postMessage biasa, sambil mengekalkan medan pembatalan, tamat masa, kemajuan, dan ralat. Ukur bahagian laluan dan kependaman ekor (tail latency).

Model jawapan berkualiti tinggi

Saya akan mengesan keupayaan dan mengesahkan keperluan konteks selamat serta pengasingan silang-asal sebelum mencipta kawasan kawalan berversi. Pengeluar menerbitkan keadaan secara atomik selepas menulis data dan memaklumkan pihak yang menunggu. Pengguna memanggil Atomics.waitAsync hanya semasa keadaan sedang menunggu, kemudian membaca semula keadaan dan urutan tanpa mengira hasil Promise. Pembatalan, tamat masa, dan penutupan adalah keadaan eksplisit. Penutupan menghentikan pengeluaran, membangkitkan pihak yang menunggu, mengesahkan Worker keluar, dan hanya selepas itu melepaskan memori kongsi. Pemilikan dan nombor urutan menghalang penggunaan pendua, manakala tingkah laku baris gilir penuh adalah pilihan produk yang diukur. Jika memori kongsi atau API tidak tersedia, ketulan Transferable menyediakan kitaran hayat dan kontrak ralat yang sama.

Kesilapan biasa

  • Menganggap notify menjamin satu panggilan bangun bagi setiap mesej.
  • Memanggil wait yang menyekat pada urutan utama dan membekukan UI.
  • Mempercayai hasil Promise tanpa membaca semula keadaan kongsi dan urutan.
  • Melepaskan penimbal serta-merta semasa pembatalan sementara Workers masih berjalan.
  • Mengabaikan kekangan pengasingan silang-asal dan sumber pihak ketiga.
  • Beralih kepada postMessage tetapi menggugurkan semantik tamat masa, pembatalan, atau ralat.

Soalan susulan

Bilakah anda akan memilih waitAsync berbanding wait?

waitAsync mengembalikan Promise tanpa menyekat urutan pemanggil, yang sesuai untuk urutan utama dan kod didorong peristiwa. wait boleh menyekat Worker, tetapi hanya apabila masa sekatan Worker tersebut tidak menjejaskan UI atau kerja kritikal lain. Kedua-duanya memerlukan semakan keadaan, tamat masa, dan protokol penutupan.

Mengapakah perlu membaca semula keadaan selepas pemberitahuan?

Pemberitahuan hanya memberitahu bahawa sesuatu keadaan mungkin telah berubah. Pemberitahuan boleh bergabung, membangunkan pesaing lain, atau sepadan dengan kumpulan terdahulu. Membaca keadaan, urutan, dan panjang mengesahkan sama ada data benar-benar tersedia.

Bagaimanakah anda menghalang kerja daripada diteruskan selepas pembatalan?

Terbitkan pembatalan dalam keadaan kongsi dan semaknya semasa menuntut slot, sebelum memproses, dan sebelum melakukan (commit) hasil. Sertakan versi tugas dalam mesej hasil; urutan utama menolak hasil daripada versi yang dibatalkan.

Bilakah memori kongsi patut ditinggalkan?

Gunakan ketulan Transferable apabila pengepala pengasingan memecahkan kebergantungan kritikal, liputan pelayar tidak mencukupi, kos penyahpepijatan melebihi faedah, atau daya pemprosesan adalah sederhana. Biarkan keupayaan dan telemetri memilih laluan dan bukannya menganggap memori kongsi sentiasa menjadi pengoptimuman.

Sumber awam

Soalan berkaitan