yield
" 关键字。它有什么用途,我该如何使用它?yield
" 关键字。它有什么用途,我该如何使用它?参考 James Long 的 "Javascript's Future: Generators" 一文中的示例,为官方 Harmony 标准进行适配:
function * foo(x) {
while (true) {
x = x * 2;
yield x;
}
}
var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16
yield
有点像return
:你会得到一些回报。 return x
返回x
的值,但是yield x
返回一个函数,它可以提供迭代到下一个值的方法。如果你有一个可能很占用内存的过程,并且你想在迭代期间中断它,这将非常有用。
function* foo(x){
。 - Rana Deep*
标记,是否需要它取决于您返回的未来类型。详细信息较长:GvR 在 Python 实现中解释了它,JavaScript 实现是基于 Python 的。使用 function *
总是正确的,但在某些情况下比具有 yield
的 function
多一些开销。 - bishopfunction *
和yield
之间的关联,并添加了引用错误(“如果在非生成器函数中出现yield或yield*表达式,则会引发早期错误”)。但是,Firefox中的原始Javascript 1.7实现不需要*
。已相应更新答案。谢谢! - bishop非常简单,它是如何工作的
yield
关键字可以帮助函数在任何时间以 异步 方式 暂停 和 恢复。看这个简单的 生成器 函数:
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');
}
[rv] = yield [expression];
expression:从生成器函数返回的值
yield any;
yield {age: 12};
rv:返回传递给生成器next()方法的可选值。
通过此机制,您可以向process()函数传递参数以执行不同的yield部分。
let val = yield 99;
_process.next(10);
now the val will be 10
用途
参考资料:
MDN 文档 在我看来相当不错。
包含 yield 关键字的函数是一个生成器。当你调用它时,它的形式参数绑定到实际参数,但它的主体并没有被实际评估。相反,返回一个生成器迭代器。每次对生成器迭代器的 next() 方法的调用都会执行迭代算法的下一步。每个步骤的值是由 yield 关键字指定的值。将 yield 视为 return 的生成器迭代器版本,表示算法的每次迭代之间的边界。每次调用 next() 时,生成器代码从 yield 语句后面的语句恢复执行。
对 Nick Sotiros 的回答进行简化/扩展(我认为他的回答非常好),我认为最好的方法是描述如何使用 yield
开始编写代码。
我个人认为,使用 yield
的最大优点是可以消除我们在代码中看到的所有嵌套回调问题。一开始很难看出来,这就是我决定写这篇答案的原因(为自己,也希望能帮到其他人!)
它所做的方式是引入协程的概念,协程是一种函数,可以自愿停止/暂停,直到它得到所需的内容。在 JavaScript 中,这由 function*
表示。只有 function*
函数可以使用 yield
。
以下是一些典型的 JavaScript 代码:
loadFromDB('query', function (err, result) {
// Do something with the result or handle the error
})
这种方法很笨重,因为现在所有的代码(显然需要等待这个 loadFromDB
调用)都需要放在这个丑陋的回调函数中。这样做有几个不好的地方....
})
function (err, result)
的术语result
另一方面,使用 yield
,借助于优美的协程框架,所有这些工作可以在一行代码内完成。
function* main() {
var result = yield loadFromDB('query')
}
因此,现在您的主函数在需要等待变量和其他内容加载时会产生(yield)结果。但是,为了运行它,您需要调用一个普通(非协程函数)。一个简单的协程框架可以解决这个问题,让您只需运行以下代码:
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
实际上只是一个关键字,您可以在回调函数之前放置它。
function* main() {
console.log(yield function(cb) { cb(null, "Hello World") })
}
会打印出“Hello World”。因此,您实际上可以通过简单地创建相同的函数签名(不带cb),并返回function (cb) {}
,将任何回调函数转换为使用yield
,如下所示:
会打印出"Hello World"。所以你可以通过创建与回调函数相同的函数签名(没有cb),并返回function(cb){}
来轻松地将任何回调函数转换为使用yield
。
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
希望通过这个知识,您可以编写更干净、更易读的代码,这样就可以 轻松删除代码!
function*
只是一个普通函数,没有yield的话就和普通函数一样。 - Honinbo Shusakufunction *
是一个包含 yield 的函数。它是一种特殊的函数,称为生成器。 - Leanderyield
的人来说,我相信这比回调函数更容易理解,但我并不认为这比回调函数更易读。 - palswimyield
关键字可以将 JavaScript 函数变为生成器函数。
JavaScript 中的生成器是指产生一系列结果而不是单个值的函数,即生成一系列值。
迭代器是能够让我们逐个访问项的方法。
迭代器如何帮助我们逐个访问项呢?它通过生成器函数来帮助我们访问项。生成器函数是指使用 yield
关键字的函数,yield 关键字可以暂停和恢复函数的执行。
以下是一个快速示例:
function *getMeDrink() {
let question1 = yield 'soda or beer'; // execution will pause here because of yield
if (question1 == 'soda') {
return 'here you get your soda';
}
if (question1 == 'beer') {
let question2 = yield 'What\'s your age'; // execution will pause here because of yield
if (question2 > 18) {
return "ok you are eligible for it";
} else {
return "Shhhh!!!!";
}
}
}
let _getMeDrink = getMeDrink(); // initialize it
_getMeDrink.next().value; // "soda or beer"
_getMeDrink.next('beer').value; // "What's your age"
_getMeDrink.next('20').value; // "ok you are eligible for it"
_getMeDrink.next().value; // undefined
让我简单解释一下发生了什么。
您注意到执行在每个yield
关键字处暂停,我们能够借助迭代器.next()
访问第一个yield
。
这逐个迭代所有yield
关键字,当没有更多的yield
关键字时返回undefined。简单地说,yield
关键字是函数暂停的断点,只有在使用迭代器调用它时才会恢复,对于我们的情况:_getMeDrink.next()
这是一个帮助我们访问函数中每个断点的迭代器示例。
生成器的示例:async/await
如果您查看 async/await
的实现,您将看到使用生成器函数和承诺
使async/await
工作,请指出任何建议均受欢迎。
为了给出一个完整的答案:yield
的作用类似于 return
,但它是在生成器中使用的。
至于通常给出的示例,它的工作原理如下:
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
,通过将其发送到生成器,在它停在第一个yield(返回0
)之后。
这使我们能够做一些非常时髦的事情。(查找协程)
yield
还可以通过协程框架来消除回调地狱。
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());
function* fibonacci() {
var a = -1, b = 1, c;
while(1) {
c = a + b;
a = b;
b = c;
yield c;
}
}
var fibonacciGenerator = fibonacci();
fibonacciGenerator.next().value; // 0
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 2
异步JavaScript调用之间的依赖关系。
yield如何使用的另一个好例子。
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()
yield
不受Internet Explorer支持。 - Krisztián Balla