เครื่องกำเนิดไฟฟ้าใน KOA

app.use ทำงานอย่างไรใน KOA เมื่อฉันตั้งค่าตัวสร้างภายใน app.use ทุกอย่างทำงานได้สมบูรณ์แบบ ฉันจะทำแบบเดียวกันที่อื่นได้อย่างไร?

เมื่อฉันเพิ่งรันคู่มือตัวสร้าง:

var getRelationsList = function *() {
  var res = yield db.relations.find({});
  console.log({'inside: ': res});
}
console.log({'outside: ': getRelationsList().next()});
getRelationsList().next();

ฉันได้รับเพียง { 'outside: ': { value: [Function], เสร็จสิ้น: false } }

นี่คือสิ่งที่ฉันคาดหวังคือ:

{ 'outside: ': { value: {object_with_results}, done: false } }
{ 'inside: ': {object_with_results}

แก้ไข

ฉันเปลี่ยนรหัสของฉันเช่นนั้น:

var getRelationsList = function *() {
  var res = yield db.relations.find({});
  console.log({'inside: ': res});
}
console.log({'outside ': co(getRelationsList)});

ตอนนี้ภายในบันทึกคอนโซลแสดงผลลัพธ์ที่ดีให้ฉัน แต่บันทึกคอนโซลภายนอกแสดงให้ฉันเห็นว่าเป็นเพียงวัตถุว่างเปล่า ป้อนคำอธิบายรูปภาพที่นี่


person Oskar Kamiński    schedule 12.05.2015    source แหล่งที่มา


คำตอบ (3)


ตัวสร้างเป็นเครื่องมือที่ทรงพลังสำหรับการจัดระเบียบโค้ดอะซิงโครนัส แต่พวกมันไม่รอให้โค้ดอะซิงโครนัสทำงานอย่างน่าอัศจรรย์

เกิดอะไรขึ้น

มาดูโค้ดของคุณเพื่อดูว่าเกิดอะไรขึ้น:

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

ปัญหาของคุณคือคุณเรียกใช้ฟังก์ชัน getRelationsList() หลายครั้งซึ่งไม่ถูกต้อง

เปลี่ยนรหัสของคุณเป็นดังต่อไปนี้

var g = getRelationsList();
console.log('outside: ', g.next());
g.next(); //You get your console.log('inside: .... here
person Molda    schedule 12.05.2015

เครื่องกำเนิดไฟฟ้าจะต้องดำเนินการโดยรหัสภายนอก ภายใต้ประทุน koa ให้ใช้ไลบรารี co เพื่อ 'เรียกใช้' ตัวสร้าง

นี่คือวิธีที่คุณอาจบรรลุสิ่งที่ต้องการนอกโคอา:

var co = require('co');
var getRelationsList = function *() {
  var res = yield db.relations.find({});
  console.log({'inside: ': res});
}

co(getRelationsList).catch(function(err){});

ฉันทำ screencast สั้น ๆ บนตัวสร้าง JavaScript ซึ่งจะช่วยให้คุณเข้าใจว่าเกิดอะไรขึ้น:

http://knowthen.com/episode-2-understand-javascript-generators/

++ แก้ไข

หากคุณใช้ตัวสร้างเพื่อเขียนโปรแกรมในรูปแบบซิงโครนัสมากขึ้น (กำจัดการเรียกกลับ) งานทั้งหมดของคุณจะต้องทำในตัวสร้างและคุณควรใช้ไลบรารีเช่น co เพื่อเรียกใช้งานตัวสร้าง

นี่คือตัวอย่างโดยละเอียดเพิ่มเติมว่าคุณจะโต้ตอบกับตัวสร้างด้วยตนเองอย่างไร สิ่งนี้จะช่วยให้คุณเข้าใจผลลัพธ์ที่คุณได้รับ

function * myGenerator () {
  var a = yield 'some value';
  return a;
}
var iterator = myGenerator();
// above line just instantiates the generator
console.log(iterator);
// empty object returned
// {}

var res1 = iterator.next();
// calling next() start the generator to either the 
// first yield statement or to return.
console.log(res1);
// res1 is an object with 2 attributes 
// { value: 'some value', done: false }
// value is whatever value was to the right of the first 
// yield statment 
// done is an indication that the generator hasn't run
// to completion... ie there is more to do
var toReturn = 'Yield returned: ' + res1.value;
var res2 = iterator.next(toReturn);
// calling next(toReturn) passes the value of 
// the variable toReturn as the return of the yield 
// so it's returned to the variable a in the generator 
console.log(res2);
// res2 is an object with 2 attributes 
// { value: 'Yield returned: some value', done: true }
// no further yield statements so the 'value' is whatever
// is returned by the generator.
// since the generator was run to completion 
// done is returned as true
person James Moore    schedule 12.05.2015
comment
ตกลง. ตอนนี้ฉันมี: { 'inside: ': {object_with_results} แต่ยังคงอยู่: { 'outside ': {} } - person Oskar Kamiński; 12.05.2015