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());