What's the yield keyword in JavaScript?
我在javascript中听说过一个"yield"关键字,但我发现它的文档非常糟糕。有人能给我解释(或推荐一个解释)它的用法和用途吗?
回答晚了,现在可能大家都知道
将James Long的"javascript的未来:生成器"中的一个例子改编为官方的和谐标准:
1 2 3 4 5 6 | function * foo(x) { while (true) { x = x * 2; yield x; } } |
"When you call foo, you get back a Generator object which has a next
method."
1 2 3 4 | var g = foo(2); g.next(); // -> 4 g.next(); // -> 8 g.next(); // -> 16 |
所以
国际海事组织,MDN文件很好。
The function containing the yield keyword is a generator. When you call it, its formal parameters are bound to actual arguments, but its body isn't actually evaluated. Instead, a generator-iterator is returned. Each call to the generator-iterator's next() method performs another pass through the iterative algorithm. Each step's value is the value specified by the yield keyword. Think of yield as the generator-iterator version of return, indicating the boundary between each iteration of the algorithm. Each time you call next(), the generator code resumes from the statement following the yield.
简化/详细描述尼克·索提罗斯的回答(我认为这很好),我认为最好描述一下如何开始用
在我看来,使用
它的方法是引入一个协同程序的概念,这是一个可以自动停止/暂停直到它得到它所需要的功能。在javascript中,它由
下面是一些典型的javascript:
1 2 3 | loadFromDB('query', function (err, result) { // Do something with the result or handle the error }) |
这是笨拙的,因为现在所有的代码(显然需要等待这个
- 所有代码缩进一级
- 你有这样一个目的,你需要追踪到每一个地方。
- 所有这些额外的
function (err, result) 行话 - 不完全清楚你这样做是为了给
result 赋值。
另一方面,使用
1 2 3 | function* main() { var result = yield loadFromDB('query') } |
所以现在,当需要等待变量和加载内容时,您的主函数将在必要时生成。但是现在,为了运行这个,您需要调用一个正常的(非协程函数)。一个简单的协同程序框架可以解决这个问题,这样您所要做的就是运行这个:
1 | start(main()) |
开始被定义(从尼克·索蒂罗的回答)
1 2 3 4 5 6 7 8 9 | function start(routine, data) { result = routine.next(data); if(!result.done) { result.value(function(err, data) { if(err) routine.throw(err); // continue next iteration of routine with an exception else start(routine, data); // continue next iteration of routine normally }); } } |
现在,您可以拥有更具可读性、易于删除且不需要修改缩进、函数等的漂亮代码。
一个有趣的观察是,在这个例子中,
1 2 3 | function* main() { console.log(yield function(cb) { cb(null,"Hello World") }) } |
将打印"Hello World"。因此,您可以通过简单地创建相同的函数签名(不带cb)并返回
1 2 3 4 5 | function yieldAsyncFunc(arg1, arg2) { return function (cb) { realAsyncFunc(arg1, arg2, cb) } } |
希望有了这些知识,您可以编写更清晰、更可读、更容易删除的代码!
很简单,就是这样
yield 关键字只帮助在任何时候异步暂停和恢复函数。- 此外,它还有助于从生成器函数返回值。
以这个简单的生成器函数为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | function* process() { console.log('Start process 1'); console.log('Pause process2 until call next()'); yield; console.log('Resumed process2'); console.log('Pause process3 until call next()'); let parms = yield {age: 12}; console.log("Passed by final process next(90):" + parms); console.log('Resumed process3'); console.log('End of the process function'); } let _process = process(); let out1 = _process.next(); console.log(out1); let out2 = _process.next(); console.log(out2); let out3 = _process.next(90); console.log(out3); |
let _process = process();
在调用_process.next()之前,它不会执行前两行代码,然后第一个yield将暂停函数。要恢复函数直到下一个暂停点(yield关键字),需要调用_process.next()。
You can think multiple yields are the breakpoints in a javascript debugger within a single function. Until
you tell to navigate next breakpoint it wont execute the code
block. (Note: without blocking the whole application)
但是,当yield执行这种暂停和恢复行为时,它也可以返回一些结果
让我们深入了解yield关键字。或者,您可以添加表达式并设置分配默认可选值。(官方文档语法)
1 | [rv] = yield [expression]; |
表达式:要从生成器函数返回的值
1 2 | yield any; yield {age: 12}; |
rv:返回传递给生成器的next()方法的可选值
Simply you can pass parameters to process() function with this mechanism, to execute different yield parts.
1 2 3 4 | let val = yield 99; _process.next(10); now the val will be 10 |
现在试试看
惯例用法
- 惰性评价
- 无限序列
- 异步控制流
参考文献:
- https://developer.mozilla.org/en-us/docs/web/javascript/reference/operators/yield
- http://javascript.tutorialhorizon.com/2015/09/16/generators-and-yield-in-es6/
- https://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/
给出一个完整的答案:
对于通常给出的示例,其工作原理如下:
1 2 3 4 5 6 7 8 9 10 11 12 | function *squareGen(x) { var i; for (i = 0; i < x; i++) { yield i*i; } } var gen = squareGen(3); console.log(gen.next().value); // prints 0 console.log(gen.next().value); // prints 1 console.log(gen.next().value); // prints 4 |
但yield关键字还有另一个用途。它可用于向生成器发送值。
为了澄清,举个小例子:
1 2 3 4 5 6 7 8 9 | function *sendStuff() { y = yield (0); yield y*y; } var gen = sendStuff(); console.log(gen.next().value); // prints 0 console.log(gen.next(2).value); // prints 4 |
这是因为值
这使我们能够找到一些真正有趣的东西。(查看连体衣)
它用于迭代器生成器。基本上,它允许您使用过程代码生成(可能是无限的)序列。请参阅Mozilla的文档。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | function start(routine, data) { result = routine.next(data); if(!result.done) { result.value(function(err, data) { if(err) routine.throw(err); // continue next iteration of routine with an exception else start(routine, data); // continue next iteration of routine normally }); } } // with nodejs as 'node --harmony' fs = require('fs'); function read(path) { return function(callback) { fs.readFile(path, {encoding:'utf8'}, callback); }; } function* routine() { text = yield read('/path/to/some/file.txt'); console.log(text); } // with mdn javascript 1.7 http.get = function(url) { return function(callback) { // make xhr request object, // use callback(null, resonseText) on status 200, // or callback(responseText) on status 500 }; }; function* routine() { text = yield http.get('/path/to/some/file.txt'); console.log(text); } // invoked as.., on both mdn and nodejs start(routine()); |
使用yield关键字的斐波那契序列生成器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function* fibbonaci(){ var a = -1, b = 1, c; while(1){ c = a + b; a = b; b = c; yield c; } } var fibonacciGenerator = fibbonaci(); fibonacciGenerator.next().value; // 0 fibonacciGenerator.next().value; // 1 fibonacciGenerator.next().value; // 1 fibonacciGenerator.next().value; // 2 |
异步JavaScript调用之间的依赖关系。
另一个很好的例子是如何使用产量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function request(url) { axios.get(url).then((reponse) => { it.next(response); }) } function* main() { const result1 = yield request('http://some.api.com' ); const result2 = yield request('http://some.otherapi?id=' + result1.id ); console.log('Your response is: ' + result2.value); } var it = main(); it.next() |