我很确定我对发电机的理解本质上已被打破。所有在线资源似乎都存在冲突,这会带来令人难以置信的困难和令人困惑的学习体验。
据我所知,yield
关键字使当前正在执行的代码块能够等待值,而不是抛出剩余的代码在回调中执行。因此,正如大多数教程所指出的那样,你可以使用它:
(function *() {
// Wait until users have be got and put into value of `results`
var results = yield db.get("users");
// And continue
view.display(results);
})();
而不是:
db.get("user", function(results) {
view.display(results);
});
是的,在我尝试编写自己的发电机之前,这一切都很好。我遇到了几个问题:
.next
,对吧?yield
似乎代表等待值最常见的用例,而在实现部分中(读取:返回值为/ db.get
)yield
似乎代表将此值发送回当前正在等待的块以恢复执行。以例如:
function *fn() {
yield 1;
yield "a";
}
var gen = fn();
gen.next(); // 1
gen.next(); // "a";
该上下文中的 yield
正在向下发送值而不是等待结果。在上面的第一个示例中,它等待db.get
和恢复执行的结果,而不是“返回”或发回一个值。如果db.get
情况属实,那么这本身并不是同步的吗?我的意思是,它不完全相同:
(function() {
//Wait for the results
var results = fs.readFileSync("users.txt");
// Use results
view.display(results);
})();
不幸的是,如果从这个问题中明确表示(可能唯一清楚的是)我不了解发电机。希望我能在这里得到一些见解。
答案 0 :(得分:21)
TL; DR:生成器的本质是控制代码执行的暂停。
对于生成器本身,您可以参考this。
总而言之,您应该区分三个组件: 发电机功能 发电机 3.生成结果
生成器函数只是function
,其头部为星形,而且(可选)yield
位于其正文中。
function *generator() {
console.log('Start!');
var i = 0;
while (true) {
if (i < 3)
yield i++;
}
}
var gen = generator();
// nothing happens here!!
生成器函数本身不执行任何操作,但返回生成器(在上面的情况下为gen
)。
此处没有控制台输出,因为只有在返回生成器的next
方法后,才会运行生成器函数的主体。
Generator有几种方法,其中最重要的方法是next
。 next
运行代码并返回生成器结果。
var ret = gen.next();
// Start!
console.log(ret);
// {value: 0, done: false}
上面的 ret
是生成器结果。它有两个属性:value
,您在生成器函数中产生的值,以及done
,一个标志,指示生成器函数是否返回。 / p>
console.log(gen.next());
// {value: 1, done: false}
console.log(gen.next());
// {value: 2, done: false}
console.log(gen.next());
// {value: undefined, done: true}
此时,没有人会指望你理解发电机,至少不是发电机的异步功率。
简单来说,生成器有两个功能:
在代码中,yield
跳转到函数之外,next(val)
跳回函数并将值传递回函数。
外部代码可以处理异步调用并决定切换到您自己的代码的适当时间。
再次查看样本:
var gen = generator();
console.log('generated generator');
console.log(gen.next().value);
// mock long long processing
setTimeout(function() {
console.log(gen.next().value);
console.log('Execute after timer fire');
}, 1000);
console.log('Execute after timer set');
/* result:
generated generator
start
0
Execute after timer set
1
Execute after timer fire
*/
请参阅?生成器函数本身不处理回调。外部代码可以。
基地就在这里。您可以详细说明此代码以支持完全异步,同时保持生成器功能,例如同步。
例如,假设geturl
是一个返回promise
对象的异步调用。你可以写var html = yield getUrl('www.stackoverflow.com');
这跳过你的代码。外部代码将执行以下操作:
var ret = gen.next();
ret.then(function (fetchedHTML) {
// jumps back to your generator function
// and assign fetchHTML to html in your code
gen.next(fetchedHTML);
});
等存储库
答案 1 :(得分:3)
没有异步的东西。生成器只是暂停和恢复代码块。当你使用我称之为https://github.com/visionmedia/co的“生成器引擎”时,会发生所有异步魔法。
基本上,gen.next()
所做的是返回最后yield
个ed值,并允许您在yield
被分配给某个内容时返回一个值,例如。 var something = yield 1
。所以如果你有代码块:
function* genfun() {
var a = yield 1
var b = yield 2
}
var gen = genfun()
gen.next() // returns the first yielded value via `{value: 1}`
gen.next(1) // sets `a` as 1, returns the next yielded value via `{value: 2}`
gen.next(2) // sets `b` as 2, the generator is done, so it returns `{done: true}`
gen.throw(err)
与next相同,除了抛出错误而不是分配给变量。
这就是控制流引擎的工作原理 - 你得到的下一个值可能是回调等等。执行回调,并且在回调完成之前不要gen.next()
。
答案 2 :(得分:1)
这两个例子不一样。当你屈服时,该函数现在实际上变成了一个回调,等待db.get(“users”)完成时执行。这样,该函数不会阻止其他函数的执行。将其视为将同步函数转换为异步函数的一种方法,让系统知道您可以在某些点暂停。