Javascript生成器:了解它们
我很确定我对发电机的理解是固有的。 所有的在线资源似乎都是冲突的,这使得一个难以置信的困难和混乱的学习经验。
根据我的理解, yield
关键字可以使当前正在执行的代码块等待一个值,而不是在callback中执行剩余的代码。 所以,正如大多数教程指出的,你可以使用这个:
(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
,对吗? - 整个API将不得不重写到支持生成器的I / O调用,对吗?
- 从我所收集的数据来看,
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); })();
不幸的是,如果从这个问题中得到任何解决方法(可能唯一清楚的是),我不了解发电机。 希望我可以在这里得到一些见解。
TL; DR:发生器的本质是控制代码执行的中止。
对于发电机本身,你可以参考这个 。
总之,有三个组成部分你应该区分:1.发电机function2.发电机3.产生的结果
发电机function简单地说就是具有星形的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
方法被调用后, 生成器函数的主体才会运行。 发生器有几种方法,其中最重要的是next
。 next
运行代码并返回生成器结果 。
var ret = gen.next(); // Start! console.log(ret); // {value: 0, done: false}
以上ret
是发生器的结果 。 它有两个属性: value
,你在generator函数中产生的值 , done
,一个表示生成器函数是否返回的标志。
console.log(gen.next()); // {value: 1, done: false} console.log(gen.next()); // {value: 2, done: false} console.log(gen.next()); // {value: undefined, done: true}
在这一点上,没有人会期望你了解发电机,至less不是发电机的asynchronous功率。
简单来说,发电机有两个特点:
- 可以select跳出某个函数,并让外部代码确定何时跳回该函数。
- asynchronous调用的控制可以在你的代码之外完成
在代码中, yield
在函数之外跳转, next(val)
跳回到函数并将值传递回函数。 外部代码可以处理asynchronous调用,并决定适当的时间切换到自己的代码。
再次看样品:
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 */
看到? 生成器函数本身不处理callback。 外面的代码呢。
基地在这里。 您可以详细说明此代码以支持完整的asynchronous性,同时保持发生器的function,如同步一个。
例如,假设geturl
是一个返回promise
对象的asynchronous调用。 你可以写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); });
有关更完整的指南,请参阅此 。 和co , galaxy , suspend等库一样
没有asynchronous的东西,如果发电机的一部分。 发生器只是暂停和恢复代码块。 当你使用我所谓的“生成器引擎”如https://github.com/visionmedia/co时,所有的asynchronous魔法都会发生。
基本上, gen.next()
所做的就是返回最后的gen.next()
值,并且如果yield
被分配给某些东西,例如ex。 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相同,只是抛出错误而不是分配给variables。
这是控制stream引擎的工作原理 – 你得到下一个可能是callback的值。 执行callback,不要gen.next()
直到callback完成。
这两个例子是不一样的。 当你屈服的时候,这个函数现在变成了一个callback函数,等待db.get(“users”)完成时执行。 这样,函数不会阻塞其他函数的执行。 把它看作是通过让系统知道你可以在某些点暂停的方法来把同步函数变成asynchronous函数的一种方法。