Pertimbangkan kode berikut yang membaca larik file secara serial / berurutan. readFiles
mengembalikan janji, yang diselesaikan hanya setelah semua file dibaca secara berurutan.
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
return new Promise((resolve, reject) =>
var readSequential = function(index) {
if (index >= files.length) {
resolve();
} else {
readFile(files[index]).then(function() {
readSequential(index + 1);
}).catch(reject);
}
};
readSequential(0); // Start!
});
};
Kode di atas berfungsi, tetapi saya tidak suka harus melakukan rekursi untuk hal-hal yang terjadi secara berurutan. Apakah ada cara yang lebih sederhana bahwa kode ini dapat ditulis ulang sehingga saya tidak harus menggunakan yang anehreadSequential
fungsi ?
Awalnya saya mencoba menggunakan Promise.all
, tetapi itu menyebabkan semua readFile
panggilan terjadi secara bersamaan, yang bukan yang saya inginkan:
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
javascript
promise
q
sequential
serial-processing
XåpplI'-I0llwlg'I -
sumber
sumber
readFileSequential()
sudah kembali sebelum yang berikutnya dipanggil (karena async, selesai lama setelah panggilan fungsi asli sudah kembali).Jawaban:
Pembaruan 2017 : Saya akan menggunakan fungsi async jika lingkungan mendukungnya:
Jika mau, Anda dapat menunda membaca file sampai Anda membutuhkannya menggunakan generator async (jika lingkungan Anda mendukungnya):
Perbarui: Dalam pemikiran kedua - saya mungkin menggunakan for loop sebagai gantinya:
Atau lebih kompak, dengan mengurangi:
Di perpustakaan janji lain (seperti kapan dan Bluebird) Anda memiliki metode utilitas untuk ini.
Misalnya, Bluebird adalah:
Meskipun sebenarnya tidak ada alasan untuk tidak menggunakan async menunggu hari ini.
sumber
Promise.resolve(Promise.resolve(15))
identik denganPromise.resolve(15)
.Inilah cara saya lebih suka menjalankan tugas secara seri.
sumber
result = result.then(task);
Pertanyaan ini sudah lama, tetapi kita hidup di dunia ES6 dan JavaScript fungsional, jadi mari kita lihat bagaimana kita dapat meningkatkannya.
Karena janji segera dieksekusi, kita tidak bisa begitu saja membuat serangkaian janji, semuanya akan diluncurkan secara paralel.
Sebaliknya, kita perlu membuat berbagai fungsi yang mengembalikan janji. Setiap fungsi kemudian akan dieksekusi secara berurutan, yang kemudian memulai janji di dalamnya.
Kita dapat memecahkan ini beberapa cara, tetapi cara favorit saya adalah menggunakan
reduce
.Ini menjadi sedikit rumit menggunakan
reduce
dalam kombinasi dengan janji-janji, jadi saya telah memecah satu liner menjadi beberapa gigitan yang lebih kecil yang dapat dicerna di bawah ini.Inti dari fungsi ini adalah menggunakan
reduce
dimulai dengan nilai awal dariPromise.resolve([])
, atau janji yang berisi array kosong.Janji ini kemudian akan diteruskan ke
reduce
metode sebagaipromise
. Ini adalah kunci untuk menghubungkan setiap janji bersama secara berurutan. Janji berikutnya untuk mengeksekusi adalahfunc
dan ketikathen
kebakaran, hasilnya disatukan dan janji itu kemudian dikembalikan, mengeksekusireduce
siklus dengan fungsi janji berikutnya.Setelah semua janji telah dieksekusi, janji yang dikembalikan akan berisi berbagai hasil dari setiap janji.
Contoh ES6 (satu liner)
Contoh ES6 (rusak)
Pemakaian:
sumber
Array.prototype.concat.bind(result)
adalah bagian yang saya lewatkan, harus melakukan mendorong ke hasil secara manual yang bekerja tetapi kurang kerenconsole.log.bind(console)
pernyataan dalam contoh terakhir Anda sekarang biasanya tidak perlu. Hari-hari ini Anda bisa lulus begitu sajaconsole.log
. Misalnya.serial(funcs).then(console.log)
. Diuji pada simpul saat ini dan Chrome.Promise.resolve([]).then((x) => { const data = mockApi('/data/1'); return Promise.resolve(x.concat(data)) }).then((x) => { const data = mockApi('/data/2'); return Promise.resolve(x.concat(data)); });
Promise.resolve([]).then(x => someApiCall('url1').then(r => x.concat(r))).then(x => someApiCall('url2').then(r => x.concat(r)))
dan sebagainyaUntuk melakukan ini cukup di ES6:
sumber
files.forEach
jika file adalah array.for (file of files) {...}
.Promise.resolve()
untuk membuat janji yang sudah terselesaikan dalam kehidupan nyata. Kenapa tidak?Promise.resolve()
tampaknya lebih bersih darinew Promise(success => success())
.Promise.resolve();
dalam kode Anda.return sequence;
saya meletakkansequence.then(() => { do stuff });
Pemanfaatan sederhana untuk janji Node.js standar:
MEMPERBARUI
item-janji adalah paket NPM siap digunakan melakukan hal yang sama.
sumber
Saya harus menjalankan banyak tugas berurutan dan menggunakan jawaban ini untuk membentuk fungsi yang akan menangani semua tugas berurutan ...
Fungsi ini mengambil 2 argumen + 1 opsional. Argumen pertama adalah array tempat kita akan bekerja. Argumen kedua adalah tugas itu sendiri, fungsi yang mengembalikan janji, tugas berikutnya akan dimulai hanya ketika janji ini terselesaikan. Argumen ketiga adalah panggilan balik untuk dijalankan ketika semua tugas telah dilakukan. Jika tidak ada panggilan balik yang dilewati, maka fungsi mengembalikan janji yang dibuatnya sehingga kami dapat menangani akhirnya.
Berikut ini contoh penggunaannya:
Semoga ini menghemat waktu ...
sumber
Solusi terbaik yang bisa saya temukan adalah dengan
bluebird
janji. Anda hanya bisa melakukanPromise.resolve(files).each(fs.readFileAsync);
yang menjamin bahwa janji-janji diselesaikan secara berurutan.sumber
Promise.each(filtes, fs.readFileAsync)
. Btw, tidak harus Anda lakukan.bind(fs)
?new Array(int)
. Semua yang dilakukan adalah presetlength
pasangan nilai kunci, yang mempengaruhi berapa banyak indeks yang digunakan selama iterasi berbasis panjang. Ia memiliki nol berpengaruh pada pengindeksan atau batas indeks array aktual)Ini sedikit variasi dari jawaban lain di atas. Menggunakan Janji asli:
Penjelasan
Jika Anda memiliki tugas ini
[t1, t2, t3]
, maka yang di atas setara denganPromise.resolve().then(t1).then(t2).then(t3)
. Itu perilaku mengurangi.Cara Penggunaan
Pertama, Anda perlu membuat daftar tugas! Tugas adalah fungsi yang tidak menerima argumen. Jika Anda harus meneruskan argumen ke fungsi Anda, gunakan
bind
atau metode lain untuk membuat tugas. Sebagai contoh:sumber
Solusi pilihan saya:
Ini tidak berbeda secara mendasar dari yang lain yang diterbitkan di sini tetapi:
Contoh penggunaan:
Diuji pada Chrome saat ini yang wajar (v59) dan NodeJS (v8.1.2).
sumber
Gunakan
Array.prototype.reduce
, dan ingatlah untuk membungkus janji-janji Anda dalam suatu fungsi jika tidak maka janji itu sudah akan berjalan!bagus dan mudah ... Anda harus dapat menggunakan kembali benih yang sama untuk kinerja, dll.
Sangat penting untuk menjaga dari array kosong atau array dengan hanya 1 elemen saat menggunakan mengurangi , jadi teknik ini adalah taruhan terbaik Anda:
dan kemudian menyebutnya seperti:
sumber
Saya membuat metode sederhana ini pada objek Janji:
Buat dan tambahkan metode Promise.afterence ke objek Promise
Pemakaian:
Hal terbaik tentang ekstensi ini ke objek Janji, adalah konsisten dengan gaya janji. Promise.all dan Promise.afterence dipanggil dengan cara yang sama, tetapi memiliki semantik yang berbeda.
Peringatan
Menjalankan janji secara berurutan biasanya bukan cara yang sangat baik untuk menggunakan janji. Biasanya lebih baik menggunakan Promise.all, dan biarkan browser menjalankan kode secepat mungkin. Namun, ada kasus penggunaan nyata untuk itu - misalnya saat menulis aplikasi seluler menggunakan javascript.
sumber
Promise.all
dan AndaPromise.sequence
. Yang satu mengambil iterable dari janji, yang lain mengambil berbagai fungsi yang mengembalikan janji.reduce
ini jawaban seperti pada Benjamin jauh lebih sederhana.Anda dapat menggunakan fungsi ini yang mendapat Daftar janjiPabrik:
Pabrik Janji hanyalah fungsi sederhana yang mengembalikan Janji:
Ini bekerja karena pabrik janji tidak menciptakan janji sampai diminta. Ini bekerja dengan cara yang sama seperti fungsi saat itu - pada kenyataannya, itu adalah hal yang sama!
Anda sama sekali tidak ingin beroperasi di atas berbagai janji. Sesuai spesifikasi Janji, segera setelah janji dibuat, janji mulai dijalankan. Jadi yang benar-benar Anda inginkan adalah berbagai pabrik janji ...
Jika Anda ingin mempelajari lebih lanjut tentang Janji, Anda harus memeriksa tautan ini: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
sumber
Jawaban saya berdasarkan https://stackoverflow.com/a/31070150/7542429 .
Solusi ini mengembalikan hasilnya sebagai array seperti Promise.all ().
Pemakaian:
sumber
Saya benar-benar menyukai jawaban @ joelnet, tetapi bagi saya, gaya pengkodean itu agak sulit dicerna, jadi saya menghabiskan beberapa hari mencoba mencari tahu bagaimana saya akan mengekspresikan solusi yang sama dengan cara yang lebih mudah dibaca dan ini adalah saya ambil, hanya dengan sintaks yang berbeda dan beberapa komentar.
sumber
Seperti yang diperhatikan Bergi, saya pikir solusi terbaik dan jelas adalah menggunakan BlueBird.each, kode di bawah ini:
sumber
Pertama, Anda perlu memahami bahwa janji dijalankan pada saat penciptaan.
Jadi misalnya jika Anda memiliki kode:
Anda perlu mengubahnya ke:
Maka kita perlu secara berantai janji-janji:
mengeksekusi
after()
, akan memastikan bahwa janji dibuat (dan dieksekusi) hanya ketika waktunya tiba.sumber
Saya menggunakan kode berikut untuk memperluas objek Janji. Ini menangani penolakan janji-janji dan mengembalikan berbagai hasil
Kode
Contoh
sumber
Jika mau, Anda dapat menggunakan mengurangi untuk membuat janji berurutan, misalnya:
itu akan selalu bekerja secara berurutan.
sumber
Menggunakan ES modern:
sumber
Dengan Async / Menunggu (jika Anda mendapat dukungan ES7)
(Anda harus menggunakan
for
lingkaran, dan tidakforEach
karena async / menunggu memiliki masalah berjalan di forEach loop)Tanpa Async / Menunggu (menggunakan Janji)
sumber
forEach
(sesuai dengan ini )Atas dasar judul pertanyaan, "Selesaikan satu demi satu janji (yaitu secara berurutan)?", Kita mungkin memahami bahwa OP lebih tertarik pada penanganan berurutan janji pada penyelesaian daripada panggilan berurutan per se .
Jawaban ini ditawarkan:
Jika panggilan bersamaan benar-benar tidak diinginkan maka lihat jawaban Benjamin Gruenbaum yang mencakup panggilan berurutan (dll) secara komprehensif.
Namun, jika Anda tertarik (untuk peningkatan kinerja) dalam pola yang memungkinkan panggilan serentak diikuti dengan penanganan respons berurutan, maka silakan baca terus.
Sangat menggoda untuk berpikir Anda harus menggunakan
Promise.all(arr.map(fn)).then(fn)
(seperti yang telah saya lakukan berkali-kali) atau gula mewah lib Promise (terutama Bluebird), namun (dengan kredit untuk artikel ini ) sebuaharr.map(fn).reduce(fn)
pola akan melakukan pekerjaan, dengan keuntungan bahwa:.then()
digunakan.Ini dia, ditulis untuk
Q
.Catatan: hanya satu fragmen itu,
Q()
yang khusus untuk Q. Untuk jQuery Anda harus memastikan bahwa readFile () mengembalikan janji jQuery. Dengan A + libs, janji-janji asing akan berasimilasi.Kuncinya di sini adalah pengurangan ini
sequence
janji, yang sekuens yang penanganan darireadFile
janji janji tetapi bukan penciptaannya.Dan begitu Anda menyerapnya, mungkin sedikit mengejutkan ketika Anda menyadari bahwa
.map()
panggung sebenarnya tidak diperlukan! Seluruh pekerjaan, panggilan paralel ditambah penanganan serial dalam urutan yang benar, dapat dicapai denganreduce()
sendirian, ditambah keuntungan tambahan dari fleksibilitas lebih lanjut untuk:Ini dia, untuk
Q
lagi.Itulah pola dasarnya. Jika Anda juga ingin mengirimkan data (misalnya file atau beberapa transformasi) ke pemanggil, Anda memerlukan varian yang ringan.
sumber
sequence.then(() => filePromise)
adalah antipattern - ia tidak menyebarkan kesalahan secepat mungkin (dan membuatunhandledRejection
lib yang mendukungnya). Anda lebih baik menggunakanQ.all([sequence, filePromise])
atau$.when(sequence, filePromise)
. Memang, perilaku ini mungkin yang Anda inginkan ketika Anda bertujuan untuk mengabaikan atau melewatkan kesalahan, tetapi Anda setidaknya harus menyebutkan ini sebagai kerugian.unhandledRejection
peristiwa. Di Bluebird Anda bisa mengatasi ini dengan menggunakansequence.return(filePromise)
yang memiliki perilaku yang sama tetapi menangani penolakan dengan baik. Saya tidak tahu referensi apa pun, saya baru saja memunculkannya - saya tidak berpikir "pola (anti)" belum memiliki nama.Pendekatan Anda tidak buruk, tetapi memang memiliki dua masalah: ia menelan kesalahan dan mempekerjakan Antipattern Konstruksi Eksplisit Janji.
Anda dapat menyelesaikan kedua masalah ini, dan membuat kode lebih bersih, sambil tetap menggunakan strategi umum yang sama:
sumber
Jika orang lain membutuhkan cara yang dijamin secara KUAT untuk menyelesaikan Janji saat melakukan operasi CRUD, Anda juga dapat menggunakan kode berikut sebagai dasar.
Selama Anda menambahkan 'kembali' sebelum memanggil setiap fungsi, mendeskripsikan Janji, dan menggunakan contoh ini sebagai dasar pemanggilan fungsi .then () selanjutnya secara KONSISTEN akan mulai setelah selesainya yang sebelumnya:
sumber
Metode push dan pop array dapat digunakan untuk urutan janji. Anda juga dapat mendorong janji baru saat Anda membutuhkan data tambahan. Ini adalah kode yang akan saya gunakan di React Infinite loader untuk memuat urutan halaman.
sumber
Sebagian besar jawaban tidak menyertakan hasil SEMUA janji secara individual, jadi jika seseorang mencari perilaku khusus ini, ini adalah solusi yang memungkinkan menggunakan rekursi.
Ini mengikuti gaya
Promise.all
:Mengembalikan array hasil dalam
.then()
panggilan balik.Jika beberapa janji gagal, segera dikembalikan dalam
.catch()
panggilan balik.Perhatikan tentang
tasks
deklarasi array :Dalam hal ini tidak mungkin untuk menggunakan notasi berikut seperti yang
Promise.all
akan digunakan:Dan kita harus menggunakan:
Alasannya adalah bahwa JavaScript mulai menjalankan janji segera setelah diumumkan. Jika kita menggunakan metode seperti
Promise.all
, itu hanya memeriksa bahwa semua dari mereka adalahfulfilled
ataurejected
, tetapi tidak memulai exection itu sendiri. Menggunakan() => promise()
kami menghentikan eksekusi sampai dipanggil.sumber
Di sini kuncinya adalah bagaimana Anda memanggil fungsi tidur. Anda harus melewati berbagai fungsi yang dengan sendirinya mengembalikan janji alih-alih array janji.
sumber
Ini untuk memperluas tentang bagaimana memproses urutan janji dengan cara yang lebih umum, mendukung urutan dinamis / tak terbatas, berdasarkan pada implementasi spex.
Tidak hanya solusi ini akan bekerja dengan urutan ukuran apa pun, tetapi Anda dapat dengan mudah menambahkan pelambatan data dan load balancing ke dalamnya.
sumber