Apakah ada masalah dengan menggunakan async
/ await
dalam satu forEach
lingkaran? Saya mencoba untuk mengulang melalui array file dan await
pada isi setiap file.
import fs from 'fs-promise'
async function printFiles () {
const files = await getFilePaths() // Assume this works fine
files.forEach(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
})
}
printFiles()
Kode ini berfungsi, tetapi bisakah ada yang salah dengan ini? Saya memiliki seseorang yang memberi tahu saya bahwa Anda tidak seharusnya menggunakan async
/ await
dalam fungsi tingkat tinggi seperti ini, jadi saya hanya ingin bertanya apakah ada masalah dengan ini.
for ... of ...
berhasil?async
/await
ke fungsi generator dan menggunakanforEach
berarti bahwa setiap iterasi memiliki fungsi generator individu, yang tidak ada hubungannya dengan yang lain. sehingga mereka akan dieksekusi secara independen dan tidak memiliki konteksnext()
dengan orang lain. Sebenarnya,for()
loop sederhana juga berfungsi karena iterasi juga dalam satu fungsi generator tunggal.await
menunda evaluasi fungsi saat ini , termasuk semua struktur kontrol. Ya, ini sangat mirip dengan generator dalam hal itu (itulah sebabnya mereka digunakan untuk polyfill async / menunggu).async
fungsi sangat berbeda dariPromise
panggilan balik pelaksana, tapi yamap
panggilan balik mengembalikan janji dalam kedua kasus.Dengan ES2018, Anda dapat sangat menyederhanakan semua jawaban di atas untuk:
Lihat spec: proposal-async-iteration
2018-09-10: Jawaban ini telah mendapat banyak perhatian baru-baru ini, silakan lihat posting blog Axel Rauschmayer untuk informasi lebih lanjut tentang iterasi asinkron: ES2018: iterasi asinkron
sumber
of
harus fungsi async yang akan mengembalikan array. Itu tidak berhasil dan Francisco berkata;Alih-alih
Promise.all
bersamaanArray.prototype.map
(yang tidak menjamin urutanPromise
penyelesaian s), saya menggunakanArray.prototype.reduce
, dimulai dengan yang diselesaikanPromise
:sumber
Promise.resolve()
danawait promise;
?Promise.resolve()
mengembalikan yang sudah diselesaikanPromise
objek, sehinggareduce
memilikiPromise
untuk memulai dengan.await promise;
akan menunggu yang terakhirPromise
dalam rantai untuk diselesaikan. @GollyJer File akan diproses secara berurutan, satu per satu.The p-iterasi modul alat NPM metode Array iterasi sehingga mereka dapat digunakan dalam cara yang sangat sederhana dengan async / Tunggulah.
Contoh dengan kasing Anda:
sumber
some
daripadaforEach
. Terima kasih!Berikut ini beberapa
forEachAsync
prototipe. Perhatikan bahwa Anda akan membutuhkannyaawait
:Catatan sementara Anda dapat memasukkan ini dalam kode Anda sendiri, Anda tidak harus memasukkan ini di perpustakaan yang Anda distribusikan kepada orang lain (untuk menghindari mencemari global mereka).
sumber
_forEachAsync
) ini masuk akal. Saya juga berpikir itu adalah jawaban terbaik karena menghemat banyak kode boilerplate.globals.js
akan baik) kita dapat menambahkan global yang kita inginkan.Selain jawaban @ Bergi , saya ingin menawarkan alternatif ketiga. Ini sangat mirip dengan contoh ke-2 @ Bergi, tetapi alih-alih menunggu masing
readFile
- masing secara individu, Anda membuat serangkaian janji, yang masing-masing Anda tunggu di bagian akhir.Perhatikan bahwa fungsi yang diteruskan
.map()
tidak perluasync
, karenafs.readFile
mengembalikan objek Janji saja. Oleh karena itupromises
adalah array objek Janji, yang dapat dikirim kePromise.all()
.Dalam jawaban @ Bergi, konsol dapat mencatat konten file sesuai urutan pembacaannya. Sebagai contoh jika file sangat kecil selesai membaca sebelum file sangat besar, itu akan dicatat terlebih dahulu, bahkan jika file kecil datang setelah file besar dalam
files
array. Namun, dalam metode saya di atas, Anda dijamin konsol akan mencatat file dalam urutan yang sama dengan array yang disediakan.sumber
await Promise.all
), tetapi file mungkin telah dibaca dalam urutan yang berbeda, yang bertentangan dengan pernyataan Anda "Anda dijamin konsol akan mencatat file dalam urutan yang sama seperti mereka Baca".Solusi Bergi bekerja dengan baik ketika
fs
didasarkan pada janji. Anda dapat menggunakanbluebird
,fs-extra
ataufs-promise
untuk ini.Namun, solusi untuk
fs
perpustakaan asli simpul adalah sebagai berikut:Catatan:
require('fs')
wajib berfungsi sebagai argumen ke-3, jika tidak menimbulkan kesalahan:sumber
Kedua solusi di atas bekerja, namun, Antonio melakukan pekerjaan dengan kode yang lebih sedikit, di sini adalah bagaimana ini membantu saya menyelesaikan data dari database saya, dari beberapa referensi anak yang berbeda dan kemudian mendorong mereka semua ke dalam array dan menyelesaikannya dalam janji setelah semua selesai:
sumber
cukup menyakitkan untuk memunculkan beberapa metode dalam file yang akan menangani data asinkron dalam urutan serial dan memberikan rasa yang lebih konvensional pada kode Anda. Sebagai contoh:
sekarang, dengan asumsi itu disimpan di './myAsync.js' Anda dapat melakukan sesuatu yang mirip dengan di bawah ini dalam file yang berdekatan:
sumber
Suka @ Bergi, tetapi dengan satu perbedaan.
Promise.all
menolak semua janji jika ada yang ditolak.Jadi, gunakan rekursi.
PS
readFilesQueue
berada di luarprintFiles
sebab efek samping * yang diperkenalkan olehconsole.log
, lebih baik mengejek, menguji, atau memata-matai, tidak keren memiliki fungsi yang mengembalikan konten (sidenote).Oleh karena itu, kode hanya dapat dirancang dengan itu: tiga fungsi terpisah yang "murni" ** dan tidak menimbulkan efek samping, memproses seluruh daftar dan dapat dengan mudah dimodifikasi untuk menangani kasus yang gagal.
Sunting / kondisi saat ini di masa depan
Node mendukung top-level menanti (ini belum memiliki plugin, tidak akan memiliki dan dapat diaktifkan melalui bendera harmoni), itu keren tetapi tidak menyelesaikan satu masalah (secara strategis saya hanya bekerja pada versi LTS). Bagaimana cara mendapatkan file?
Menggunakan komposisi. Mengingat kode itu, membuat saya merasa bahwa ini ada di dalam modul, jadi, harus memiliki fungsi untuk melakukannya. Jika tidak, Anda harus menggunakan IIFE untuk membungkus kode peran ke dalam fungsi async membuat modul sederhana yang melakukan semuanya untuk Anda, atau Anda dapat pergi dengan cara yang benar, yaitu, komposisi.
Perhatikan bahwa nama variabel berubah karena semantik. Anda melewatkan functor (fungsi yang dapat dipanggil oleh fungsi lain) dan menerima pointer pada memori yang berisi blok awal logika aplikasi.
Tetapi, jika ini bukan modul dan Anda perlu mengekspor logikanya?
Bungkus fungsi dalam fungsi async.
Atau ubah nama variabel, apa pun ...
*
by side effect menans efek kolakteral dari aplikasi yang dapat mengubah status / perilaku atau memperkenalkan bug dalam aplikasi, seperti IO.**
oleh "pure", itu dalam tanda kutip karena fungsi itu tidak murni dan kode dapat dikonversikan ke versi murni, ketika tidak ada output konsol, hanya manipulasi data.Selain itu, untuk menjadi murni, Anda harus bekerja dengan monad yang menangani efek samping, yang rentan kesalahan, dan memperlakukan kesalahan itu secara terpisah dari aplikasi.
sumber
Satu peringatan penting adalah:
await + for .. of
Metode danforEach + async
caranya sebenarnya memiliki efek yang berbeda.Setelah
await
di dalamfor
loop nyata akan memastikan semua panggilan async dieksekusi satu per satu. DanforEach + async
caranya akan memadamkan semua janji pada saat yang sama, yang lebih cepat tetapi terkadang kewalahan ( jika Anda melakukan permintaan DB atau mengunjungi beberapa layanan web dengan batasan volume dan tidak ingin memecat 100.000 panggilan sekaligus).Anda juga bisa menggunakan
reduce + promise
(kurang elegan) jika Anda tidak menggunakanasync/await
dan ingin memastikan file dibaca satu demi satu .Atau Anda dapat membuat forEachAsync untuk membantu tetapi pada dasarnya menggunakan hal yang sama untuk loop yang mendasarinya.
sumber
forEach
- mengakses indeks alih-alih mengandalkan iterability - dan meneruskan indeks ke callback.Array.prototype.reduce
cara yang menggunakan fungsi async. Saya telah menunjukkan contoh dalam jawaban saya: stackoverflow.com/a/49499491/2537258Dengan menggunakan Task, futurize, dan Daftar yang dapat dilalui, Anda dapat melakukannya
Inilah cara Anda mengaturnya
Cara lain untuk menyusun kode yang diinginkan adalah
Atau mungkin lebih berorientasi fungsional
Kemudian dari fungsi induk
Jika Anda benar-benar menginginkan lebih banyak fleksibilitas dalam penyandian, Anda bisa melakukan ini (untuk bersenang-senang, saya menggunakan operator Pipe Forward yang diusulkan )
PS - Saya tidak mencoba kode ini di konsol, mungkin memiliki beberapa kesalahan ketik ... "gaya bebas lurus, dari atas kubah!" seperti yang dikatakan anak-anak tahun 90-an. :-p
sumber
Saat ini properti prototipe Array.forEach tidak mendukung operasi async, tetapi kami dapat membuat poly-fill kami sendiri untuk memenuhi kebutuhan kami.
Dan itu dia! Anda sekarang memiliki metode async forEach yang tersedia pada array apa pun yang didefinisikan setelah ini untuk operasi.
Ayo kita coba ...
Kita dapat melakukan hal yang sama untuk beberapa fungsi array lainnya seperti peta ...
... dan seterusnya :)
Beberapa hal yang perlu diperhatikan:
Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>
tidak akan memiliki fitur inisumber
Hanya menambah jawaban asli
sumber
Untuk melihat bagaimana itu bisa salah, cetak console.log di akhir metode.
Hal-hal yang bisa salah secara umum:
Ini tidak selalu salah tetapi sering dalam kasus penggunaan standar.
Secara umum, menggunakan forEach akan menghasilkan semua kecuali yang terakhir. Ini akan memanggil setiap fungsi tanpa menunggu fungsi yang berarti memberitahu semua fungsi untuk memulai kemudian selesai tanpa menunggu fungsi selesai.
Ini adalah contoh dalam JS asli yang akan menjaga ketertiban, mencegah fungsi kembali sebelum waktunya dan secara teori mempertahankan kinerja yang optimal.
Ini akan:
Dengan solusi ini, file pertama akan ditampilkan segera setelah tersedia tanpa harus menunggu yang lain tersedia terlebih dahulu.
Ini juga akan memuat semua file pada saat yang sama daripada harus menunggu yang pertama selesai sebelum membaca file kedua dapat dimulai.
Satu-satunya kelemahan dari ini dan versi aslinya adalah bahwa jika banyak pembacaan dimulai sekaligus maka lebih sulit untuk menangani kesalahan karena memiliki lebih banyak kesalahan yang dapat terjadi pada suatu waktu.
Dengan versi yang membaca file pada suatu waktu maka akan berhenti pada kegagalan tanpa membuang waktu mencoba membaca file lagi. Bahkan dengan sistem pembatalan yang rumit, bisa sulit untuk menghindarinya gagal pada file pertama tetapi sudah membaca sebagian besar file lainnya juga.
Kinerja tidak selalu dapat diprediksi. Sementara banyak sistem akan lebih cepat dengan membaca file paralel beberapa akan lebih suka berurutan. Beberapa dinamis dan mungkin bergeser di bawah beban, optimisasi yang menawarkan latensi tidak selalu menghasilkan throughput yang baik di bawah pertengkaran berat.
Tidak ada penanganan kesalahan dalam contoh itu. Jika sesuatu mengharuskan mereka untuk berhasil ditampilkan atau tidak, itu tidak akan berhasil.
Eksperimen mendalam dianjurkan dengan console.log pada setiap tahap dan solusi pembacaan file palsu (sebagai gantinya penundaan). Meskipun banyak solusi tampaknya melakukan hal yang sama dalam kasus-kasus sederhana semua memiliki perbedaan halus yang memerlukan beberapa pemeriksaan ekstra untuk memeras.
Gunakan tiruan ini untuk membantu membedakan antara solusi:
sumber
Hari ini saya menemukan beberapa solusi untuk ini. Menjalankan async menunggu fungsi di forEach Loop. Dengan membangun pembungkus di sekitar kita dapat mewujudkannya.
Penjelasan lebih rinci tentang cara kerjanya secara internal, untuk forach asli dan mengapa tidak dapat membuat panggilan fungsi async dan rincian lainnya tentang berbagai metode disediakan di tautan di sini
Berbagai cara yang melaluinya dapat dilakukan dan mereka adalah sebagai berikut,
Metode 1: Menggunakan pembungkus.
Metode 2: Menggunakan yang sama sebagai fungsi generik Array.prototype
Array.prototype.forEachAsync.js
Penggunaan:
Metode 3:
Menggunakan Promise.all
Metode 4: Tradisional untuk loop atau modern untuk loop
sumber
Promise.all
seharusnya digunakan - mereka tidak memperhitungkan banyak kasus tepi.Promise.all
.Promise.all
tidak mungkin tetapiasync
/await
adalah. Dan tidak,forEach
sama sekali tidak menangani kesalahan janji.Solusi ini juga dioptimalkan untuk memori sehingga Anda dapat menjalankannya pada 10.000 item data dan permintaan. Beberapa solusi lain di sini akan membuat server crash pada kumpulan data besar.
Dalam TypeScript:
Cara Penggunaan?
sumber
Anda dapat menggunakan
Array.prototype.forEach
, tetapi async / menunggu tidak begitu kompatibel. Ini karena janji yang dikembalikan dari panggilan balik async mengharapkan untuk diselesaikan, tetapiArray.prototype.forEach
tidak menyelesaikan janji apa pun dari pelaksanaan panggilan baliknya. Jadi, Anda bisa menggunakan forEach, tetapi Anda harus menangani sendiri resolusi janji.Berikut adalah cara untuk membaca dan mencetak setiap file secara seri menggunakan
Array.prototype.forEach
Berikut adalah cara (masih menggunakan
Array.prototype.forEach
) untuk mencetak konten file secara paralelsumber
Mirip dengan Antonio Val's
p-iteration
, modul npm alternatif adalahasync-af
:Atau,
async-af
memiliki metode statis (log / logAF) yang mencatat hasil janji:Namun, keuntungan utama perpustakaan adalah Anda dapat membuat metode asinkron untuk melakukan sesuatu seperti:
async-af
sumber
Saya akan menggunakan modul pify dan async (jutaan unduhan per minggu) yang teruji dengan baik . Jika Anda tidak terbiasa dengan modul async, saya sangat menyarankan Anda memeriksa dokumennya . Saya telah melihat banyak devs membuang-buang waktu untuk membuat ulang metodenya, atau lebih buruk lagi, membuat kode async sulit dipelihara ketika metode async tingkat tinggi akan menyederhanakan kode.
sumber