Petunjuk dan konteks
Pertanyaan coding ini menguji penjadwalan konkurensi daripada sekadar memasukkan setiap promise ke dalam Promise.all. Kandidat harus mendefinisikan kontrak untuk throughput, pengurutan, propagasi error, pembatalan, dan input kosong, lalu menunjukkan bahwa batas tidak pernah terlampaui dan tidak ada item yang diproses dua kali.
Apa yang dievaluasi pewawancara
- Apakah worker tetap atau scheduler yang setara mempertahankan paling banyak N tugas aktif.
- Apakah hasil ditulis berdasarkan indeks input sehingga urutan penyelesaian tidak mengubah urutan output.
- Apakah semantik fail-fast dan pengumpulan error bersifat eksplisit, termasuk untuk tugas yang sudah berjalan.
- Apakah nilai N yang tidak valid, input kosong, throw sinkron, pembatalan, dan nilai mapper non-Promise dapat ditangani dengan benar.
Pertanyaan klarifikasi yang perlu diajukan
Konfirmasikan apakah tugas boleh berlanjut setelah pembatalan, apakah fail-fast menunggu pembersihan, apakah hasil parsial dikembalikan, dan apakah mapper menerima AbortSignal. Tanyakan tentang retry, konkurensi dinamis, dan mempertahankan identitas objek error asli.
Kerangka jawaban 30 detik
Saya akan membuat tidak lebih dari N worker yang berbagi penghitung indeks berikutnya. Setiap worker mengambil indeks, membungkus pengembalian dan throw sinkron dengan Promise.resolve, dan menulis berdasarkan indeks. Fail-fast mempropagasikan error pertama yang ditemukan dan menghentikan pengiriman baru; mode pengumpulan mencatat setiap status. Pembatalan menghentikan pengambilan baru dan meneruskan sinyal ke mapper. Beban kerja adalah O(items), ruang hasil dan status adalah O(items), dan pekerjaan aktif dibatasi oleh O(N).
Pembahasan mendalam langkah demi langkah
1. Tentukan kontrak hasil dan error
Hasil tetap mempertahankan urutan input. Mode pengumpulan mengembalikan status fulfilled atau rejected dengan nilai atau alasan asli; undefined tidak dapat menjadi penanda keberhasilan. Fail-fast mengembalikan error pertama yang diamati, dengan mengingat bahwa JavaScript tidak dapat menghentikan promise yang sudah dimulai secara paksa. Mapper yang menerima sinyal dapat bekerja sama dengan pembatalan.
2. Gunakan worker tetap dan alokasi indeks
Simpan indeks berikutnya dalam state bersama. Setiap worker melakukan loop hingga indeks berada di luar rentang, pembatalan diatur, atau fail-fast menghentikan pengiriman. Tingkatkan nilai indeks sebelum memulai tugas sehingga satu item hanya dapat diambil satu kali. Gunakan min(N, items.length) worker untuk menghindari promise yang menganggur.
3. Tangani throw sinkron dan urutan penyelesaian
Tangkap throw mapper sinkron dan normalisasi nilai maupun promise dengan Promise.resolve. Tulis keberhasilan atau kegagalan ke indeks asli; jangan pernah melakukan push dari callback penyelesaian. Begitu satu worker selesai, ia akan mengambil indeks berikutnya, menjaga throughput tanpa menambah pekerjaan aktif.
4. Propagasikan pembatalan dan hentikan pengiriman
Jika sinyal sudah dibatalkan (aborted), tolak atau kembalikan error pembatalan yang terdokumentasi sebelum memulai pekerjaan. Tugas yang sedang berjalan menerima sinyal yang sama. Scheduler menunggu worker keluar sebelum menyelesaikan promise utamanya, sehingga pemanggil tidak melihat penyelesaian saat loop latar belakang masih terus mengirimkan tugas. Pembatalan dan error bisnis memerlukan penyebab yang dapat dibedakan.
5. Verifikasi kasus batas dan kompleksitas
Uji array kosong, N sama dengan nol, N lebih besar dari input, nilai dan throw sinkron, variasi keterlambatan, kegagalan pertama, banyak kegagalan, dan pembatalan di tengah jalan. Gunakan penghitung aktif untuk memastikan nilainya tidak pernah melebihi N dan hook untuk memastikan setiap indeks berjalan sekali. Pemanggilan mapper adalah O(items), ruang hasil adalah O(items), dan konkurensi scheduler adalah O(N).
Contoh jawaban yang kuat
Saya memvalidasi bahwa N adalah bilangan bulat positif, membuat min(N, items.length) worker, dan membagikan penghitung indeks berikutnya. Worker mengambil indeks, memanggil mapper melalui Promise.resolve agar nilai dan throw sinkron mengikuti jalur yang sama, dan menulis hasilnya berdasarkan indeks. Fail-fast menyetel flag hentikan-pengiriman dan mempropagasikan error pertama; mode pengumpulan menyimpan setiap status. Pembatalan mencegah pengambilan baru dan meneruskan sinyal ke mapper aktif, lalu menunggu worker selesai. Pengujian mencakup keterlambatan yang tidak berurutan, error sinkron, input kosong, batas-batas N, dan pembatalan, dengan asersi bahwa pekerjaan aktif tidak pernah melebihi N.
Kesalahan umum
- Memanggil
Promise.all(items.map(mapper))dan memulai semua tugas sekaligus. - Melakukan push pada hasil sehingga output mengikuti urutan penyelesaian, bukan urutan input.
- Hanya menangkap penolakan promise dan melewatkan throw mapper sinkron.
- Terus mengirimkan tugas setelah fail-fast, atau berasumsi bahwa penolakan secara otomatis membatalkan pekerjaan yang sedang berjalan.
- Memperlakukan pembatalan sebagai error bisnis biasa sehingga pemanggil tidak dapat memilih perilaku retry.
- Melewatkan validasi untuk N, input kosong, dan nilai mapper non-Promise biasa.
Pertanyaan lanjutan dan jawaban
Mengapa tidak memulai item berikutnya secara rekursif?
Rekursi dapat mengekspresikan rantai serial, tetapi juga harus mengelola konkurensi, error, dan pembatalan. Worker tetap membuat pembuktian tugas aktif menjadi lebih jelas serta menghindari rekursi mendalam atau pengiriman duplikat.
Apa yang terjadi pada tugas yang sudah berjalan selama fail-fast?
Promise JavaScript tidak memiliki primitif penghentian paksa secara umum. Hentikan pengiriman baru dan panggil abort saat mapper bekerja sama dengan AbortSignal; jika tidak, biarkan tugas selesai tanpa menulis ke hasil yang sudah diselesaikan secara eksternal.
Bagaimana mode pengumpulan seharusnya merepresentasikan error?
Kembalikan objek status per item yang membedakan fulfilled dan rejected sambil mempertahankan nilai atau alasan aslinya. Nilai sentinel tidak aman karena nilai yang berhasil itu sendiri bisa berupa undefined atau null.
Bagaimana Anda menyesuaikan konkurensi secara dinamis?
Lacak batas maksimum yang dikonfigurasi secara terpisah dari pekerjaan aktif, lalu tambahkan atau hentikan pengambilan pada batas yang aman. Penyesuaian dinamis menambah biaya pembuktian dan pengujian, jadi gunakan batas tetap kecuali jika persyaratan secara eksplisit membutuhkan adaptasi.