1. Soalan dan konteks
Anda mesti memproses iterator segerak yang mungkin mengandungi berjuta-juta rekod: menapis baris yang tidak sah, memetakkannya kepada model paparan, mengambil 100 yang pertama, dan mengira jumlah keseluruhan. Kod asal telah menyebarkan (spread) sumber ke dalam tatasusunan sebelum memanggil map, filter, dan reduce, menyebabkan lonjakan memori yang tinggi. Tulis semula kod tersebut dengan JavaScript Iterator Helpers dan terangkan sifat lazy, protokol iterator, penamatan awal, pembersihan, dan sandaran untuk persekitaran masa jalan yang lebih lama.
2. Perkara yang dinilai oleh penemu duga
- Sama ada anda memahami perbezaan antara Iterator dan Iterable, dan bahawa Iterator Helpers mengembalikan iterator yang boleh terus digunakan.
- Sama ada anda boleh menggabungkan map, filter, take, find, reduce, dan toArray ke dalam satu paip lazy.
- Sama ada anda tahu bahawa iterator mempunyai keadaan (stateful) dan biasanya hanya bergerak ke hadapan sekali, serta penamatan awal memberi peluang kepada kaedah return yang mendasari untuk melakukan pembersihan.
- Sama ada anda mengendalikan sumber yang tidak terhingga atau mahal, pengecualian, keserasian, dan sempadan di mana penjelmaan tatasusunan adalah wajar.
3. Penjelasan untuk ditanya sebelum menjawab
- Adakah sumber tersebut merupakan Iterator segerak, Iterable, atau API berhalaman tak segerak?
- Adakah hasilnya mesti dikembalikan sebagai satu tatasusunan, atau bolehkah pemanggil terus menggunakannya sebagai strim?
- Selepas mengambil N hasil, adakah kursor rangkaian, fail, atau pangkalan data mesti ditutup?
- Adakah versi Node dan pelayar sasaran menyediakan Iterator Helpers asli, dan adakah polyfill dibenarkan?
4. Kerangka jawapan 30 saat
Saya akan menormalkan Iterable kepada Iterator, kemudian merangkaikan filter, map, dan take, hanya memanggil toArray pada sempadan yang benar-benar memerlukan tatasusunan. map dan filter tidak melintasi secara serta-merta; reduce dan toArray memulakan penggunaan. Sebaik sahaja take mencapai hadnya, paip tersebut harus berhenti menarik data dan menggunakan penutupan iterator supaya sumber dapat dilepaskan. Iterator mempunyai keadaan dan tidak boleh dikongsi sewenang-wenangnya antara pengguna. Persekitaran masa jalan yang lebih lama boleh menggunakan polyfill terkawal atau pelaksanaan penjana dengan semantik yang sama, yang diuji untuk pengecualian, penamatan awal, dan penggunaan memori pada sumber yang besar.
5. Jawapan mendalam langkah demi langkah
Langkah 1: Membezakan Iterator dan Iterable
Iterable menyediakan kaedah Symbol.iterator yang boleh menghasilkan Iterator; Iterator menyediakan next dan mengembalikan done serta value. Iterator.from menormalkan input yang mematuhi protokol lelaran. Iterator Helpers beroperasi pada iterator dan mencipta pembantu lazy; ia tidak menyalin keseluruhan sumber ke dalam tatasusunan secara automatik.
Langkah 2: Membina paip lazy untuk map, filter, dan take
Fungsi di bawah membaca sumber hanya semasa hasilnya sedang digunakan. filter memeriksa baris, map mengubahnya, dan take berhenti selepas kiraan yang diminta dicapai supaya rekod yang tidak berkaitan tidak ditarik.
function topAmounts(source, limit) {
return Iterator.from(source)
.filter((row) => row.status === "paid")
.map((row) => ({ id: row.id, amount: row.cents / 100 }))
.take(limit);
}
const firstHundred = topAmounts(records(), 100).toArray();Langkah 3: Memahami masa penggunaan dan keadaan satu laluan (one-pass)
Mencipta pembantu tidak memanggil fungsi panggilan balik. Menarik next, atau memanggil forEach, find, reduce, atau toArray, akan menggunakan nilai sumber. Iterator menyimpan kedudukan semasa; pengguna pertama mengubah keadaannya dan pengguna kedua mungkin melihat iterator yang telah habis. Cipta iterator sumber baharu untuk hasil yang bebas dan bukannya berkongsi tika yang telah digunakan.
Langkah 4: Mengendalikan penamatan awal, return, dan pengecualian
Operasi seperti take dan find boleh berhenti selepas memperoleh sesuatu hasil. Jika iterator yang mendasari menyediakan return, pembantu harus memberikannya peluang untuk menutup sumber semasa selesai atau gagal, seperti pemegang fail atau kursor halaman. Kod perniagaan masih perlu melepaskan sumber miliknya dalam blok finally, dan mengesahkan pembersihan apabila fungsi panggilan balik melontarkan ralat atau pengguna berhenti awal.
Langkah 5: Memilih sempadan penjelmaan dan sandaran
toArray menjelmakan baki hasil, jadi letakkannya hanya di tempat di mana capaian rawak, pensirilan, atau rendering kelompok memerlukan tatasusunan. Iterator tidak terhingga, halaman yang besar, dan pengiraan yang mahal harus kekal lazy. Jika persekitaran masa jalan kekurangan Iterator Helpers asli, gunakan polyfill terkawal atau pembungkus penjana untuk map, filter, dan take. Kekalkan penggunaan satu laluan, penamatan awal, dan penyebaran pengecualian; jangan tukar setiap sumber kepada tatasusunan secara rahsia.
6. Contoh jawapan berkualiti tinggi
Saya akan menormalkan input dengan Iterator.from, kemudian merangkaikan filter, map, dan take, hanya memanggil toArray pada sempadan output yang memerlukan tatasusunan. Panggilan balik pembantu berjalan semasa penggunaan, jadi sumber yang besar tidak dikembangkan lebih awal; take atau find harus berhenti menarik data dan menggunakan return untuk membolehkan kursor yang mendasari ditutup. Iterator mempunyai keadaan dan biasanya untuk kegunaan sekali sahaja, jadi hasil yang bebas memerlukan iterator sumber baharu. Bagi Node atau pelayar yang lebih lama, saya akan menggunakan polyfill atau pelaksanaan penjana dengan semantik lazy dan penutupan yang sama, serta menguji pengecualian, pemberhentian awal, pelepasan sumber, dan lonjakan memori.
7. Kesilapan lazim
- Menyebarkan (spreading) sumber terlebih dahulu → seluruh sumber dijelmakan → kekalkan toArray pada sempadan yang benar-benar memerlukan tatasusunan.
- Menganggap penciptaan map melaksanakan panggilan balik → kesan sampingan tidak berlaku semasa persediaan → jelaskan bahawa penggunaan mencetuskan penarikan data.
- Menggunakan semula satu iterator → hasil kedua kosong atau separa → cipta sumber baharu bagi setiap pengguna.
- Terus meminta halaman selepas take → rangkaian dan sumber dibazirkan → sahkan bahawa penamatan awal memanggil return yang mendasari.
- Polyfill yang hanya menyalin hasil tatasusunan → semantik sumber tidak terhingga dan pengecualian berubah → kekalkan sifat lazy, keadaan satu laluan, dan penyebaran pengecualian.
8. Soalan susulan dan respons
Soalan susulan 1: Apakah perbezaan utama antara Iterator Helpers dan kaedah tatasusunan?
Kaedah tatasusunan beroperasi pada tatasusunan yang telah dijelmakan dan biasanya melintasi serta-merta. Iterator Helpers menerima iterator dan melakukan transformasi lazy seperti map, filter, dan take, hanya menarik elemen yang diminta oleh pengguna.
Soalan susulan 2: Bilakah anda masih perlu memanggil toArray?
Panggil kaedah ini apabila sempadan memerlukan pengindeksan rawak, pensirilan, API kelompok yang hanya menerima tatasusunan, atau rendering sekali sahaja yang kecil. Elakkan penjelmaan bagi sumber yang terlalu besar atau tidak terhingga dan teruskan penggunaan secara lazy.
Soalan susulan 3: Mengapakah iterator tidak boleh digunakan semula sewenang-wenangnya?
Ia menyimpan kursor, dan next mengubah keadaan dalamannya. Selepas seorang pengguna membacanya, pengguna kedua akan menerima kedudukan yang tinggal. Untuk memainkannya semula, iterator baharu yang dihasilkan daripada Iterable diperlukan.
Soalan susulan 4: Bagaimanakah anda mengesahkan bahawa penamatan awal melepaskan sumber?
Gunakan iterator ujian yang mengira penarikan dan merekodkan panggilan return. Selepas take atau find, pastikan penarikan berhenti dan return dijalankan, kemudian uji kes pengecualian panggilan balik dan gangguan pengguna.