JavaScript中的yield关键字是什么?
我在JavaScript中听说过一个“yield”关键字,但是我发现文档很差。 有人可以解释我的(或build议一个网站,解释)其用法和用途?
MDN文档相当不错,IMO。
包含yield关键字的函数是一个生成器。 当你调用它的时候,它的forms参数绑定到实际的参数,但是它的实体并没有被实际评估。 而是返回一个生成器迭代器。 每次调用生成器迭代器的next()方法都会执行迭代algorithm的另一次遍历。 每个步骤的值都是yield关键字指定的值。 将yield看作return的生成器迭代器版本,指示algorithm的每次迭代之间的边界。 每次调用next()时,生成器代码都会从yield之后的语句中恢复。
晚回答,可能大家现在都知道yield
,但是有一些更好的文档已经出现了。
根据官方的Harmony标准,用James Long的“Javascript的未来:发电机”作为例子:
function * foo(x) { while (true) { x = x * 2; yield x; } }
“当你调用foo时,你会找回一个具有下一个方法的Generator对象。”
var g = foo(2); g.next(); // -> 4 g.next(); // -> 8 g.next(); // -> 16
所以, yield
有点像return
:你得到回报。 return x
返回return x
的值,但yield x
返回一个函数,该函数为您提供了一个方法来迭代下一个值。 如果您有一个潜在的内存密集型过程 ,在迭代过程中您可能想要中断,那么这很有用。
简化/详细说明Nick Sotiros的答案(我认为这很好),我认为最好是描述一下如何以yield
开始编码。
在我看来,使用yield
的最大优点是它将消除我们在代码中看到的所有嵌套的callback问题。 起初很难看出,这就是为什么我决定写这个答案(对于我自己,希望其他人!)
这样做的方式是引入一个协同例程的思想,这个function可以自动停止/暂停,直到获得所需的function。 在JavaScript中,这是由function*
表示。 只有function*
函数可以使用yield
。
以下是一些典型的javascript:
loadFromDB('query', function (err, result) { // Do something with the result or handle the error })
这是笨重的,因为现在你所有的代码(显然需要等待这个loadFromDB
调用)都需要在这个丑陋的callback中。 这有几个原因是不好的…
- 所有的代码都缩进一层
- 你有这个结局
})
,你需要跟踪到处 - 所有这些额外的
function (err, result)
行话 - 不完全清楚,你正在做这个分配一个值的
result
另一方面,有了yield
,所有这一切都可以在一个很好的协同框架的帮助下完成。
function* main() { var result = yield loadFromDB('query') }
所以现在当你需要等待variables和事物加载的时候,你的主函数将会在必要的时候产生。 但是现在,为了运行这个,你需要调用一个正常的 (非协程函数)。 一个简单的协同例程框架可以解决这个问题,所以你所要做的就是运行这个:
start(main())
开始被定义(来自Nick Sotiro的回答)
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 }); } }
而现在,你可以拥有漂亮的代码,可读性更强,易于删除,而且不需要剔除缩进,函数等。
一个有趣的观察是,在这个例子中, yield
实际上只是一个关键字,你可以把它放在一个带有callback函数的函数之前。
function* main() { console.log(yield function(cb) { cb(null, "Hello World") }) }
将打印“你好世界”。 所以你可以通过简单地创build相同的函数签名(没有cb)和返回function (cb) {}
来将任何callback函数转换为使用yield
,如下所示:
function yieldAsyncFunc(arg1, arg2) { return function (cb) { realAsyncFunc(arg1, arg2, cb) } }
希望借助这些知识,您可以编写清晰易读, 易于删除的代码!
它用于迭代器生成器。 基本上,它允许您使用程序代码来创build(可能是无限的)序列。 请参阅Mozilla的文档 。
要给出一个完整的答案: yield
的工作原理与return
类似,但是在一个generator中。
至于通常给出的例子,这个工作如下:
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关键字的第二个目的。 它可以用来发送值到发生器。
澄清,一个小例子:
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
这个工作,因为值2
被分配给y
,通过发送它到发生器,它停在第一个产量(返回0
)之后。
这使我们能够一些非常时髦的东西。 (查看协程)
-
yield
关键字只是简单地帮助暂停和恢复一个函数在任何时间asynchronous 。 - 此外,它有助于从发电机function 返回值 。
以这个简单的发电机function:
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()'); yield; console.log('Resumed process3'); console.log('End of the process function'); }
让_process = process();
直到你调用_process.next()它不会执行前两行代码,那么第一个yield将暂停函数。 要恢复该function, 直到下一个暂停点( yield关键字 ),您需要调用_process.next() 。
你可以认为多个产量是一个函数内的JavaScriptdebugging器的断点 。 直到你告诉导航下一个断点,它不会执行代码块。 ( 注意 :不阻挡整个应用程序)
但是当yield执行这个暂停并恢复行为时,它也可以返回一些结果 {value: any, done: boolean}
根据前面的函数,我们还没有发出任何值。 如果我们探索前面的输出,它会显示相同的{ value: undefined, done: false }
,其值为undefined 。
让我们深入到yield关键字。 或者,您可以添加expression式并设置一个默认的可选值 。 (官方文档语法)
[rv] = yield [expression];
expression式 :从生成器函数返回的值
yield any; yield {age: 12};
rv :返回传递给生成器的next()方法的可选值
let val = yield 99; _process.next(10); now the val will be 10
用法
- 懒惰的评价
- 无限序列
- asynchronous控制stream
参考文献:
yield
也可以用来消除callback地狱,用一个协程框架。
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());