我在JavaScript中听说过“yield”关键字,但我发现它的文档很差。有人可以向我解释(或推荐一个解释的网站)其用法及其用途吗?
答案 0 :(得分:172)
迟到回答,现在可能每个人都知道yield
,但有一些更好的文档已经出现。
根据官方和谐标准改编詹姆斯·朗的"Javascript's Future: Generators"示例:
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
返回x
的值,但yield x
返回一个函数,该函数为您提供了迭代下一个值的方法。如果您有一个potentially memory intensive procedure,您可能希望在迭代期间中断它,这很有用。
答案 1 :(得分:81)
MDN documentation非常好,IMO。
包含yield关键字的函数是一个生成器。当你调用它时,它的形式参数绑定到实际的参数,但它的实体并没有被实际评估。而是返回一个generator-iterator。每次调用generator-iterator的next()方法都会执行迭代算法的另一次传递。每个步骤的值是yield关键字指定的值。将yield视为返回的generator-iterator版本,指示算法的每次迭代之间的边界。每次调用next()时,生成器代码将从yield之后的语句中恢复。
答案 2 :(得分:51)
简化/阐述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')
}
现在,当需要等待变量和事物加载时,你的主函数将在必要时产生。但是现在,为了运行它,你需要调用 normal (非协程函数)。一个简单的协同例程框架可以解决这个问题,所以你要做的就是运行它:
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)并返回yield
来实际将任何回调函数转换为使用function (cb) {}
,如下所示:
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
希望有了这些知识,您可以编写更清晰,更易读的代码easy to delete!
答案 3 :(得分:46)
这真的很简单,这就是它的工作方式
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');
}
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);
让_process = process();
在您致电 _process.next() 之前,不会执行前2行代码,然后第一次产量将暂停该功能。 要恢复功能直到下一个暂停 点(产生关键字),您需要调用 _process.next() 强>
您可以认为在单个函数中的javascript调试器中,多个产量是断点。直到 你告诉导航下一个断点它不会执行代码 块。 (注意:不阻止整个应用程序)
但是当yield执行此暂停并恢复行为时,它可以返回一些结果以及{value: any, done: boolean}
根据前面的函数,我们没有发出任何值。如果我们探索之前的输出,它将显示相同的{ value: undefined, done: false }
值未定义。
让我们深入了解yield关键字。您可以选择添加表达式并设置分配默认的可选值。 (官方文档语法)
[rv] = yield [expression];
表达式:从生成器函数返回的值
yield any;
yield {age: 12};
rv :返回传递给生成器next()方法的可选值
只需使用此机制将参数传递给process()函数,即可执行不同的屈服部分。
let val = yield 99;
_process.next(10);
now the val will be 10
<强> 用法 强>
参考文献:
答案 4 :(得分:17)
要给出完整的答案: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
,通过将其发送到生成器,在第一次收益停止后(返回0
)。
这使我们能够获得一些非常时髦的东西。 (查找协程)
答案 5 :(得分:16)
它用于迭代器生成器。基本上,它允许您使用过程代码创建(可能是无限的)序列。请参阅Mozilla's documentation。
答案 6 :(得分:6)
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());
答案 7 :(得分:6)
Yeild
关键字使其成为生成器,
javaScript中的生成器是什么?
生成器是一种生成结果序列而不是单个值的函数,即您生成一系列值
含义生成器可以帮助我们与帮助迭代器异步工作,哦,现在,hack迭代器是什么?真的吗?
迭代器是指我们能够一次访问一个项目的意思
迭代器从哪里帮助我们一次访问一项? 它帮助我们通过生成器功能访问项目,
生成器函数是我们使用yeild
关键字的那些函数,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 'Whats 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 // "Whats your age"
_getMeDrink.next('20').value // "ok you are eligible for it"
_getMeDrink.next().value // undefined
让我Brifly解释发生了什么
您发现每个yeild
关键字都被暂停执行
并且我们可以在迭代器yield
的帮助下首先访问.next()
此操作一次遍历所有yield
个关键字,然后在没有更多yield
个关键字时返回undefined
简单地说,您可以说yield
关键字是断点,函数每次都暂停,并且仅在使用迭代器调用时恢复
对于我们的情况:_getMeDrink.next()
这是迭代器的示例,它可以帮助我们访问函数中的每个断点
发电机示例:
async/await
如果看到async/await
的实施
您将看到generator functions & promises
用于使async/await
工作
请指出所有建议
答案 8 :(得分:4)
使用yield关键字的Fibonacci序列生成器。
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
答案 9 :(得分:2)
异步javascript调用之间的依赖关系。
另一个如何使用良率的好例子。
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()
答案 10 :(得分:0)
在了解产量之前,您需要了解发电机。生成器是使用function*
语法创建的。生成器函数不执行代码,而是返回一种称为生成器的迭代器类型。当使用next
方法给定值时,生成器函数将继续执行,直到遇到yield关键字为止。使用yield
将为您提供一个包含两个值的对象,一个是value,另一个是完成的(布尔值)。该值可以是数组,对象等。
答案 11 :(得分:0)
一个简单的例子:
const strArr = ["red", "green", "blue", "black"];
const strGen = function*() {
for(let str of strArr) {
yield str;
}
};
let gen = strGen();
for (let i = 0; i < 5; i++) {
console.log(gen.next())
}
//prints: {value: "red", done: false} -> 5 times with different colors, if you try it again as below:
console.log(gen.next());
//prints: {value: undefined, done: true}
答案 12 :(得分:0)
我还试图了解yield关键字。根据我目前的理解,在生成器中,yield关键字的工作方式类似于CPU上下文切换。运行yield语句时,将保存所有状态(例如,局部变量)。
除此之外,直接结果对象将返回给调用方,例如{value:0,done:false}。调用者可以使用该结果对象通过调用next()来决定是否再次“唤醒”生成器(调用next()是迭代执行)。
另一个重要的事情是它可以为局部变量设置一个值。 “唤醒”生成器时,“ next()”调用者可以传递此值。例如it.next('valueToPass'),如下所示:“ resultValue = yield slowQuery(1);”就像唤醒下一个执行时一样,调用者可以向执行中注入一些运行结果(将其注入局部变量)。因此,对于此执行,存在两种状态:
上次执行时保存的上下文。
此执行的触发器注入的值。
因此,使用此功能,生成器可以整理出多个异步操作。通过设置局部变量(上例中的resultValue),将第一个异步查询的结果传递给第二个异步查询。第二个异步查询只能由第一个异步查询的响应触发。然后,第二个异步查询可以检查局部变量值以决定下一步,因为该局部变量是第一个查询的响应中的注入值。
异步查询的困难在于:
回调地狱
缺少上下文,除非在回调中将它们作为参数传递。
yield和generator可以同时提供帮助。
没有yield和generator时,要分类多个异步查询,需要使用参数作为上下文的嵌套回调,这不容易阅读和维护。
下面是一个与nodejs一起运行的链式异步查询示例:
const axios = require('axios');
function slowQuery(url) {
axios.get(url)
.then(function (response) {
it.next(1);
})
.catch(function (error) {
it.next(0);
})
}
function* myGen(i=0) {
let queryResult = 0;
console.log("query1", queryResult);
queryResult = yield slowQuery('https://google.com');
if(queryResult == 1) {
console.log("query2", queryResult);
//change it to the correct url and run again.
queryResult = yield slowQuery('https://1111111111google.com');
}
if(queryResult == 1) {
console.log("query3", queryResult);
queryResult = yield slowQuery('https://google.com');
} else {
console.log("query4", queryResult);
queryResult = yield slowQuery('https://google.com');
}
}
console.log("+++++++++++start+++++++++++");
let it = myGen();
let result = it.next();
console.log("+++++++++++end+++++++++++");
以下是运行结果:
+++++++++++ start +++++++++++
query1 0
+++++++++++ end +++++++++++
query2 1
query4 0
下面的状态模式可以为上面的示例做类似的事情:
const axios = require('axios');
function slowQuery(url) {
axios.get(url)
.then(function (response) {
sm.next(1);
})
.catch(function (error) {
sm.next(0);
})
}
class StateMachine {
constructor () {
this.handler = handlerA;
this.next = (result = 1) => this.handler(this, result);
}
}
const handlerA = (sm, result) => {
const queryResult = result; //similar with generator injection
console.log("query1", queryResult);
slowQuery('https://google.com');
sm.handler = handlerB; //similar with yield;
};
const handlerB = (sm, result) => {
const queryResult = result; //similar with generator injection
if(queryResult == 1) {
console.log("query2", queryResult);
slowQuery('https://1111111111google.com');
}
sm.handler = handlerC; //similar with yield;
};
const handlerC = (sm, result) => {
const queryResult = result; //similar with generator injection;
if (result == 1 ) {
console.log("query3", queryResult);
slowQuery('https://google.com');
} else {
console.log("query4", queryResult);
slowQuery('https://google.com');
}
sm.handler = handlerEnd; //similar with yield;
};
const handlerEnd = (sm, result) => {};
console.log("+++++++++++start+++++++++++");
const sm = new StateMachine();
sm.next();
console.log("+++++++++++end+++++++++++");
以下是运行结果:
+++++++++++ start +++++++++++
query1 0
+++++++++++ end +++++++++++
query2 1
query4 0
答案 13 :(得分:0)
不要忘记非常有用的“ x of generator”语法来循环遍历生成器。完全不需要使用next()函数。
function* square(x){
for(i=0;i<100;i++){
x = x * 2;
yield x;
}
}
var gen = square(2);
for(x of gen){
console.log(x);
}