Menggunakan untuk menunggu ... dengan iterables sinkron

11

MDN mengatakan for await...of memiliki dua kasus penggunaan:

The for await...ofpernyataan menciptakan loop iterasi async iterable objek serta pada iterables sync, ...

Saya sebelumnya mengetahui yang pertama: async iterables using Symbol.asyncIterator. Tapi saya sekarang tertarik pada yang terakhir: iterables sinkron.

Kode berikut ini berulang di atas iterable sinkron - sebuah array janji. Tampaknya untuk memblokir proses pada pemenuhan setiap janji.

async function asyncFunction() {
    try {
        const happy = new Promise((resolve)=>setTimeout(()=>resolve('happy'), 1000))
        const sad = new Promise((_,reject)=>setTimeout(()=>reject('sad')))
        const promises = [happy, sad]
        for await(const item of promises) {
            console.log(item)
        }
    } catch (err) {
        console.log(`an error occurred:`, err)
    }
}

asyncFunction() // "happy, an error occurred: sad" (printed in quick succession, after about 5 seconds)

Perilaku ini tampaknya sama dengan menunggu setiap janji pada gilirannya, sesuai dengan logika yang ditunjukkan di bawah ini. Apakah pernyataan ini benar?

Saya bertanya karena pola kode ini memiliki jebakan penolakan tersirat Promise.alldan Promise.allSettledmenghindarinya, dan sepertinya aneh bagi saya bahwa pola ini akan didukung secara eksplisit oleh bahasa.

Ben Aston
sumber
2
Apa sebenarnya pertanyaan Anda? Sepertinya contoh yang Anda berikan berhasil
Sagi Rika
Apakah deskripsi saya for await... ofdengan iterables sinkron, benar, dan jika demikian, apakah penting bahwa pola itu dapat memancarkan kesalahan penolakan yang tidak ditangani?
Ben Aston
"Apakah ini benar" bukan pertanyaan. "Benar" adalah apa pun yang Anda katakan itu.
Robert Harvey
Bisakah Anda mendemonstrasikan dengan memancarkan kesalahan penolakan tidak tertangani yang Anda jelaskan melalui kode?
Robert Harvey
Kode terakhir menunjukkannya. Benar memiliki makna yang terdefinisi dengan baik dalam konteks ini karena saya telah menyediakan kode untuk menggambarkan apa yang saya pikir sedang dilakukan. Jika perilaku cocok dengan kode saya, maka kode saya benar, jika tidak pemahaman saya salah. Juga pengamatan "Benar" adalah apa pun yang Anda katakan itu. jelas tidak benar. Benar memiliki arti yang didefinisikan dengan baik dalam konteks ini.
Ben Aston

Jawaban:

4

Ya, ini aneh, dan Anda seharusnya tidak melakukan ini. Jangan mengulangi susunan janji, itu justru mengarah pada masalah penolakan-ditangani yang Anda sebutkan .

Jadi mengapa ini didukung dalam bahasa? Untuk melanjutkan semantik janji yang ceroboh.

Anda dapat menemukan alasan yang tepat dalam komentar tentang masalah yang membahas bagian proposal ini :

Saya pikir kita harus kembali ke Symbol.iteratorsemantik karena semantik Janji kita saat ini adalah tentang memungkinkan sinkronisasi hal-hal yang digunakan sebagai hal-hal async. Anda mungkin menyebutnya "kecerobohan". Ini mengikuti logika @ groundwater di atas , tapi saya hanya ingin menjelaskan paralelnya dengan lebih detail.

Semantik "rantai" .thensemua tentang ini. Anda dapat mengembalikan Janji .thenatau dari nilai skalar; semuanya sama. Anda menelepon Promise.resolveuntuk tidak membungkus sesuatu dalam Janji, tetapi untuk melemparkan sesuatu ke Janji - dapatkan nilai asinkron ketika Anda memiliki sesuatu-atau-yang lain.

Semantik asyncdan awaitsemua tentang menjadi ceroboh juga. Anda dapat menampar awaitekspresi non-Janji apa pun dalam fungsi async dan semuanya berfungsi dengan baik, persis dengan cara yang sama, kecuali bahwa Anda memberikan kontrol ke antrian pekerjaan. Demikian pula, Anda dapat "mempertahankan" menempatkan async apa pun yang Anda inginkan, selama Anda awaithasilnya. Jika Anda memiliki fungsi yang mengembalikan Janji - apa pun! Anda dapat menjadikannya asyncfungsi, dan, dari perspektif pengguna, tidak ada yang berubah (bahkan jika, secara teknis, Anda mendapatkan objek Janji yang berbeda).

Iterator dan generator Async harus bekerja dengan cara yang sama. Seperti halnya Anda dapat menunggu nilai yang, secara tidak sengaja, bukan Janji, pengguna yang wajar akan mengharapkan dapat yield*menyinkronkan iterator dalam generator async. for awaitloop juga harus "bekerja" jika pengguna secara defensif menandai loop dengan cara itu, berpikir bahwa mereka mungkin mendapatkan iterator async.

Saya pikir itu akan menjadi masalah besar untuk memecahkan semua persamaan ini. Itu akan membuat iterator async kurang ergonomis. Mari kita bahas ini lain kali ketika generator / iterator async muncul dalam agenda di TC39.

Bergi
sumber
Terima kasih. Apakah suatu peristiwa dipancarkan, atau itu sebenarnya semacam kesalahan lain? Saya bertanya karena saya pikir acara adalah bagian dari WebAPI. Apakah emisi acara digunakan dengan cara yang serupa, di bagian lain dari spesifikasi?
Ben Aston
@ 52d6c6af Apakah Anda mengacu pada unhandledrejectionacara?
Bergi
Iya. Hanya saja untuk mencegat "kesalahan" yang saya gunakan window.addEventListener('unhandledrejection',...Singkatnya: itu adalah satu-satunya contoh yang bisa saya ingatkan, dari jenis emisi kesalahan oleh JavaScript. Saya hampir pasti salah untuk memikirkan ini. Akhirnya: apakah emisi "kesalahan" ini benar-benar penting selain memiliki pesan kesalahan yang tidak diinginkan di konsol?
Ben Aston
1
@ 52d6c6af Lihat di sini dan di sana untuk bagaimana ini ditentukan dalam upaya bersama antara ECMAScript dan Web API spesifikasi. Tidak, acara tidak terlalu penting, sudah terlambat ketika Anda mendapatkannya. Afaics, ini hanya digunakan untuk memonitor kesalahan sisi klien.
Bergi
Jika tidak terlalu penting, apakah sarannya masih "jangan mengulangi susunan janji", atau apakah itu lebih "sadar bahwa ini tidak menunjukkan perilaku gagal-puasa dalam beberapa keadaan"?
Ben Aston
0

The sadjanji tidak menjadi awaited ketika gagal - bahwa kebutuhan kode untuk menyelesaikan menunggu happysebelum dapat mulai menunggu sad. The sadJanji gagal sebelum happyresolve. ( Promise.alladalah alat yang lebih cocok untuk kasus penggunaan ini)

Gershom
sumber
1
Aku tahu. Karena itu pertanyaanku. Jika Promise.allini merupakan solusi yang lebih baik, mengapa bahasa tersebut memenuhi sintaksis ini? for await...ofbisa dengan mudah diimplementasikan untuk hanya menghitung iterables asynchronous. Tapi mereka melayani untuk menghitung iterables sinkron (tetapi dengan perangkap (tampak?)). Mengapa?
Ben Aston
1
Ah, saya salah paham. Apakah kita bertanya mengapa for await ... ofmenerima iterables sinkron? Saya membayangkan mendukung generator async yang secara kondisional dapat mengembalikan item sinkron.
Gershom
Ya, terutama mengingat bahwa hal itu tampaknya memperkenalkan perangkap perangkap penolakan.
Ben Aston
Menurut pendapat saya, jebakan lebih umum dalam menciptakan janji, dan tidak segera menantinya. Sayangnya jebakan ini juga sering merupakan fitur yang sangat berguna.
Gershom