Generator adalah alat yang ampuh untuk mengatur kode asinkron, tetapi generator tidak secara ajaib menunggu kode asinkron dijalankan.
Apa yang terjadi
Mari kita telusuri kode Anda sehingga Anda dapat melihat apa yang terjadi:
getRelationsList
adalah fungsi generator, yang ketika dipanggil akan mengembalikan generator baru. Pada titik ini, tidak ada kode dalam fungsi generator Anda yang dipanggil (walaupun jika Anda meneruskan params, kode tersebut akan disetel). Anda kemudian memanggil .next
pada generator Anda untuk memulai eksekusi fungsi generator. Ini akan dijalankan hingga mencapai pernyataan hasil pertama dan mengembalikan objek dengan nilai yang dihasilkan dan status penyelesaian generator.
Tampaknya Anda memahami sebagian besar dari hal itu sejauh ini, tetapi generator tidak secara ajaib mengubah nilai yang dihasilkan. Saat Anda menghasilkan db.relations.find({})
, Anda akan mendapatkan nilai kembalian dari fungsi find
yang saya asumsikan adalah Janji atau sejenisnya:
jadi nilai 'luar' Anda adalah { value:Promise, done:false }
Alasan mengapa console.log
bagian dalam Anda tidak pernah berjalan adalah karena Anda sebenarnya membuat generator baru setiap kali Anda memanggil getRelationsList()
, jadi ketika Anda memanggil getRelationsList().next()
lagi setelah bagian luar console.log
Anda membuat generator baru dan memanggil berikutnya, sehingga hanya dijalankan hingga hasil pertama, sama seperti panggilan pada baris sebelumnya.
Untuk menyelesaikan eksekusi, Anda harus memanggil next dua kali pada instance generator yang sama: sekali untuk mengeksekusi hingga hasil dan sekali untuk melanjutkan eksekusi hingga akhir fungsi.
var gen = getRelationsList()
gen.next() // { value:Promise, done:false }
gen.next() // { value:undefined, done:true } (will also console.log inside)
Anda akan melihat, namun jika Anda menjalankan ini, bagian dalam console.log
akan menjadi undefined
. Itu karena nilai pernyataan yield
sama dengan nilai yang diteruskan ke panggilan .next()
berikutnya.
Misalnya:
var gen2 = getRelationsList()
gen2.next() // { value:Promise, done:false }
gen2.next(100) // { value:undefined, done:true }
Keluaran
{ inside:100 }
Karena kita meneruskan 100 ke panggilan .next()
kedua dan itu menjadi nilai pernyataan yield db.relations.find({})
yang kemudian ditetapkan ke res
.
Berikut tautan yang mendemonstrasikan semua ini: http://jsfiddle.net/qj1aszub/2/
Solusinya
Pembuat koa menggunakan perpustakaan kecil bernama co yang pada dasarnya menerima janji yang telah dihasilkan dan menunggu hingga janji tersebut selesai sebelumnya meneruskan nilai yang diselesaikan kembali ke fungsi generator (menggunakan fungsi .next()
) sehingga Anda dapat menulis kode asinkron dalam gaya sinkron.
co akan mengembalikan janji, yang mengharuskan Anda memanggil metode .then
untuk mendapatkan nilai yang dikembalikan dari fungsi generator.
var co = require('co');
var getRelationsList = function *() {
var res = yield db.relations.find({});
console.log({'inside: ': res});
return res
}
co(getRelationsList).then(function(res) {
console.log({'outside: ': res })
}).catch(function(err){
console.log('something went wrong')
});
co juga memungkinkan Anda untuk menghasilkan fungsi generator lainnya dan menunggu penyelesaiannya, sehingga Anda tidak perlu menyelesaikan semuanya dengan co di setiap level dan menangani janji hingga Anda berada di 'level teratas':
co(function *() {
var list = yield getRelationsList()
, processed = yield processRelations(list)
, response = yield request.post('/some/api', { data:processed })
return reponse.data
}).then(function(data) {
console.log('got:', data)
})
person
mlrawlings
schedule
12.05.2015