我听说过JavaScript中有一个“ yield”关键字,但是我发现关于它的文档很差。有人可以向我解释(或推荐一个可以解释其用途的网站)及其用途吗?
我听说过JavaScript中有一个“ yield”关键字,但是我发现关于它的文档很差。有人可以向我解释(或推荐一个可以解释其用途的网站)及其用途吗?
Answers:
IMO ,MDN文档非常好。
包含yield关键字的函数是一个生成器。当您调用它时,它的形式参数绑定到实际参数,但是它的主体没有被实际评估。相反,将返回generator-iterator。对生成器迭代器的next()方法的每次调用都会通过迭代算法执行另一遍。每个步骤的值是yield关键字指定的值。将yield视为返回的生成器-迭代器版本,指示算法每次迭代之间的边界。每次调用next()时,生成器代码都会从yield之后的语句恢复。
答案很晚,也许每个人都知道yield
,但是随之而来的是一些更好的文档。
改编James Long的“ Javascript的未来:生成器”中的示例作为官方Harmony标准:
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
返回一个函数,该函数为您提供了一个方法来迭代下一个值。如果您有可能要在迭代过程中中断的可能占用大量内存的过程,则很有用。
function* foo(x){
那儿
*
标记。是否需要它取决于您所返回的未来类型。细节很长:GvR在Python实现中对此进行了解释,并以此为基础对Javascript实现进行建模。使用function *
始终是正确的,尽管在某些情况下,开销要比function
使用稍微多一些yield
。
真的很简单,就是这样
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');
}
让_process = process();
在调用_process.next()之前,它不会执行代码的前2行,然后第一个yield将暂停该函数。要将函数恢复到下一个暂停点(yield关键字),您需要调用_process.next()。
您可以认为多个收益是单个函数中javascript调试器中的断点。在您告诉导航下一个断点之前,它不会执行代码块。(注意:不阻塞整个应用程序)
但是,尽管yield执行此暂停和恢复行为时,它也可以
根据以前的函数返回一些结果,而{value: any, done: boolean}
我们没有发出任何值。如果我们探索先前的输出,它将显示相同{ value: undefined, done: false }
的值undefined。
让我们深入到yield关键字。(可选)您可以添加表达式并设置分配默认的可选值。(官方文档语法)
[rv] = yield [expression];
expression:从生成器函数返回的值
yield any;
yield {age: 12};
rv:返回传递给生成器的next()方法的可选值
只需使用此机制即可将参数传递给process()函数,以执行不同的屈服部分。
let val = yield 99;
_process.next(10);
now the val will be 10
用法
参考文献:
简化/详细说明尼克·索蒂罗斯(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')
}
因此,现在,当需要等待变量和事物加载时,主函数将在需要时产生。但是现在,为了运行此命令,您需要调用一个普通的(非协程函数)。一个简单的协同例程框架可以解决此问题,因此您只需执行以下操作:
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”。因此,您实际上可以yield
通过简单地创建相同的函数签名(不带cb)并返回来使用任何回调函数function (cb) {}
,如下所示:
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
希望有了这些知识,您可以编写更清晰,更易读的易于删除的代码!
function*
只是没有收益的正则函数?
function *
是包含 yield 的函数。这是一个特殊功能,称为生成器。
yield
在任何地方使用过的人来说,我相信这比回调更有意义,但是我看不出它比回调更易读。
给出完整的答案: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
。
这使我们能够进行一些非常时髦的事情。(查找协程)
用于迭代器生成器。基本上,它允许您使用过程代码来构成(可能是无限的)序列。请参阅Mozilla的文档。
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());
使用yield关键字的斐波那契序列生成器。
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
Yeild
javaScript函数中的关键字使其成为生成器,
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
让我飞扬一下,这是怎么回事
您注意到每个yeild
关键字的执行都已暂停,我们可以yield
在迭代器的帮助下首先访问.next()
这yield
一次一次迭代所有关键字,然后yield
在简单单词中不再有关键字时返回undefined,您可以说yield
关键字是断点,函数每次暂停时仅在使用迭代器调用时才恢复
对于我们的情况:_getMeDrink.next()
这是迭代器的示例,它可以帮助我们访问函数中的每个断点
发电机示例:
async/await
如果您看到实施,async/await
您将看到generator functions & promises
用于制作async/await
工作
请指出任何建议,欢迎
异步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()
在了解产量之前,您需要了解发电机。生成器是使用function*
语法创建的。生成器函数不执行代码,而是返回一种称为生成器的迭代器类型。当使用next
方法给定值时,生成器函数将继续执行,直到遇到yield关键字为止。使用yield
会给您返回一个包含两个值的对象,一个是value,另一个是完成的(布尔值)。该值可以是数组,对象等。
一个简单的例子:
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}
我也试图了解yield关键字。根据我目前的理解,在生成器中,yield关键字的工作方式类似于CPU上下文切换。运行yield语句时,将保存所有状态(例如,局部变量)。
除此之外,直接结果对象将返回给调用者,例如{value:0,done:false}。调用者可以使用此结果对象来决定是否通过调用next()再次“唤醒”生成器(调用next()是要迭代执行)。
另一个重要的事情是它可以将值设置为局部变量。“唤醒”生成器时,“ next()”调用者可以传递此值。例如it.next('valueToPass'),例如:“ resultValue = yield slowQuery(1);” 就像唤醒下一个执行时一样,调用者可以向执行中注入一些运行结果(将其注入局部变量)。因此,对于此执行,存在两种状态:
上次执行时保存的上下文。
由该执行的触发器注入的值。
因此,借助此功能,生成器可以整理出多个异步操作。通过设置局部变量(上例中的resultValue),将第一个异步查询的结果传递给第二个异步查询。第二个异步查询只能由第一个异步查询的响应触发。然后,第二个异步查询可以检查局部变量值来决定下一步,因为该局部变量是从第一个查询的响应中注入的值。
异步查询的困难在于:
回调地狱
除非在回调中将它们作为参数传递,否则它们会失去上下文。
产量和发电机都可以提供帮助。
如果没有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+++++++++++");
运行结果如下:
+++++++++++开始+++++++++++
查询1 0
+++++++++++结束+++++++++++
查询2 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+++++++++++");
以下是运行结果:
+++++++++++开始+++++++++++
查询1 0
+++++++++++结束+++++++++++
查询2 1
query4 0