ตัวสร้างเป็นเครื่องมือที่ทรงพลังสำหรับการจัดระเบียบโค้ดอะซิงโครนัส แต่พวกมันไม่รอให้โค้ดอะซิงโครนัสทำงานอย่างน่าอัศจรรย์
เกิดอะไรขึ้น
มาดูโค้ดของคุณเพื่อดูว่าเกิดอะไรขึ้น:
getRelationsList
เป็นฟังก์ชันตัวสร้าง ซึ่งเมื่อถูกเรียกจะส่งกลับตัวสร้างใหม่ ณ จุดนี้ ไม่มีการเรียกโค้ดในฟังก์ชันตัวสร้างของคุณ (แม้ว่าคุณจะส่งพารามิเตอร์ก็ตาม โค้ดนั้นก็จะถูกตั้งค่า) จากนั้นคุณโทร .next
บนตัวสร้างของคุณเพื่อเริ่มการทำงานของฟังก์ชันตัวสร้าง มันจะดำเนินการจนกว่าจะถึงคำสั่ง Yield แรก และส่งคืนอ็อบเจ็กต์ที่มีค่า Yielded และสถานะความสมบูรณ์ของตัวสร้าง
ดูเหมือนว่าคุณจะเข้าใจเรื่องส่วนใหญ่จนถึงตอนนี้ แต่เครื่องกำเนิดไฟฟ้าไม่ได้แปลงค่าที่ได้ออกมาอย่างน่าอัศจรรย์ เมื่อคุณให้ผลตอบแทน db.relations.find({})
คุณจะได้รับค่าส่งคืนของฟังก์ชัน find
ซึ่งฉันสมมติว่าเป็น Promise หรือบางประเภทที่สามารถดำเนินการได้:
ดังนั้นค่า 'ภายนอก' ของคุณคือ { value:Promise, done:false }
เหตุผลที่ console.log
ภายในของคุณไม่เคยทำงานเลยก็คือคุณกำลังสร้างตัวสร้างใหม่ทุกครั้งที่คุณโทร getRelationsList()
ดังนั้นเมื่อคุณโทร getRelationsList().next()
อีกครั้งหลังจากภายนอก console.log
คุณกำลังสร้างตัวสร้างใหม่และโทรตัวถัดไป ดังนั้นมันจึงทำงานได้ไม่เกิน อัตราผลตอบแทนแรก เช่นเดียวกับการโทรในบรรทัดก่อนหน้า
เพื่อให้การดำเนินการเสร็จสิ้น คุณต้องเรียก next สองครั้งบนอินสแตนซ์เดียวกันของตัวสร้างของคุณ: หนึ่งครั้งเพื่อดำเนินการจนถึงอัตราผลตอบแทน และอีกครั้งเพื่อดำเนินการดำเนินการต่อไปจนสิ้นสุดฟังก์ชัน
var gen = getRelationsList()
gen.next() // { value:Promise, done:false }
gen.next() // { value:undefined, done:true } (will also console.log inside)
อย่างไรก็ตาม คุณจะสังเกตเห็นว่าหากคุณเรียกใช้สิ่งนี้ ภายใน console.log
จะเป็น undefined
นั่นเป็นเพราะว่าค่าของคำสั่ง yield
เท่ากับค่าที่ส่งไปยังการเรียก .next()
ต่อไปนี้
ตัวอย่างเช่น:
var gen2 = getRelationsList()
gen2.next() // { value:Promise, done:false }
gen2.next(100) // { value:undefined, done:true }
เอาท์พุต
{ inside:100 }
เนื่องจากเราส่งผ่าน 100 ไปยังการเรียก .next()
ครั้งที่สอง และนั่นกลายเป็นค่าของคำสั่ง yield db.relations.find({})
ซึ่งต่อมาถูกกำหนดให้กับ res
นี่คือลิงก์สาธิตทั้งหมดนี้: http://jsfiddle.net/qj1aszub/2/
การแก้ไขปัญหา
ผู้สร้าง koa ใช้ไลบรารีเล็กๆ ที่เรียกว่า co ซึ่งโดยพื้นฐานแล้วจะรับคำสัญญาที่ให้ไว้และรอให้พวกเขาดำเนินการให้เสร็จสิ้นก่อน ส่งผ่านค่าที่แก้ไขแล้วกลับไปยังฟังก์ชันตัวสร้าง (โดยใช้ฟังก์ชัน .next()
) เพื่อให้คุณสามารถเขียนโค้ดอะซิงโครนัสในรูปแบบซิงโครนัสได้
co จะส่งคืนสัญญา ซึ่งคุณจะต้องเรียกใช้เมธอด .then
เพื่อรับค่าที่ส่งคืนจากฟังก์ชันตัวสร้าง
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 ยังช่วยให้คุณสร้างฟังก์ชันตัวสร้างอื่น ๆ และรอให้มันเสร็จสิ้น ดังนั้นคุณไม่จำเป็นต้องห่อสิ่งต่าง ๆ ด้วย co ในทุกระดับและจัดการกับสัญญาจนกว่าคุณจะอยู่ที่ 'ระดับบนสุด':
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