什么';JavaScript中的yield关键字是什么?

什么';JavaScript中的yield关键字是什么?,javascript,yield,keyword,Javascript,Yield,Keyword,我听说JavaScript中有一个“yield”关键字,但我发现关于它的文档非常糟糕。有人能给我解释一下(或者推荐一个网站来解释)它的用法和它的用途吗?它用于迭代器生成器。基本上,它允许您使用过程代码生成(可能是无限的)序列。看。在我看来,天气相当不错 包含yield关键字的函数是一个生成器。当您调用它时,它的形式参数被绑定到实际参数,但它的主体并没有被实际计算。而是返回一个生成器迭代器。对生成器迭代器的next()方法的每次调用都会执行迭代算法的另一次传递。每个步骤的值都是由yield关键字指

我听说JavaScript中有一个“yield”关键字,但我发现关于它的文档非常糟糕。有人能给我解释一下(或者推荐一个网站来解释)它的用法和它的用途吗?

它用于迭代器生成器。基本上,它允许您使用过程代码生成(可能是无限的)序列。看。

在我看来,天气相当不错

包含yield关键字的函数是一个生成器。当您调用它时,它的形式参数被绑定到实际参数,但它的主体并没有被实际计算。而是返回一个生成器迭代器。对生成器迭代器的next()方法的每次调用都会执行迭代算法的另一次传递。每个步骤的值都是由yield关键字指定的值。将yield视为return的生成器迭代器版本,指示算法每次迭代之间的边界。每次调用next(),生成器代码都会从yield后面的语句恢复


回答晚了,可能现在每个人都知道
yield
,但是一些更好的文档已经出现了

将James Long的例子改编为官方和谐标准:

function * foo(x) {
    while (true) {
        x = x * 2;
        yield x;
    }
}
“当您调用foo时,将返回一个生成器对象,该对象具有下一个 方法。”


所以,
yield
有点像
return
:你得到了回报
return x
返回
x
的值,但是
yield x
返回一个函数,该函数为您提供了一个方法,可以迭代到下一个值。如果您有一个在迭代过程中可能需要中断的问题,则此选项非常有用。

要给出完整的答案:
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
当值
2
被分配给
y
,并在第一次产量停止后(返回
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* main() {
  var result = yield loadFromDB('query')
}

简化/详细说明Nick Sotiros的答案(我认为这非常棒),我认为最好描述一下如何开始使用
yield
进行编码

在我看来,使用
yield
的最大优点是它将消除我们在代码中看到的所有嵌套回调问题。很难看出一开始是怎么回事,这就是为什么我决定写这个答案(为了我自己,希望也是为了其他人!)

它的工作方式是引入一个共同例程的概念,这是一个可以自动停止/暂停的函数,直到它得到它所需要的。在javascript中,这由
函数*
表示。只有
function*
函数才能使用
yield

下面是一些典型的javascript:

loadFromDB('query', function (err, result) {
  // Do something with the result or handle the error
})
这是笨拙的,因为现在所有的代码(显然需要等待这个
loadFromDB
调用)都需要在这个丑陋的回调中。这不好有几个原因

  • 您的所有代码都缩进一级
  • 你有这样一个目的})你需要在任何地方跟踪它
  • 所有这些额外的
    函数(err,result)
    术语
  • 不太清楚您这样做是为了给
    result
另一方面,有了
yield
,所有这些都可以在nice协同例程框架的帮助下在一行中完成

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* main() {
  var result = yield loadFromDB('query')
}
现在,当需要等待变量和内容加载时,主函数将在必要的地方产生。但是现在,为了运行它,您需要调用一个普通的(非协程函数)。一个简单的协同例程框架可以解决此问题,因此您只需运行以下操作:

start(main())
定义了启动(来自Nick Sotiro的回答)

现在,您可以拥有更具可读性、易于删除且无需修改缩进、函数等的漂亮代码

一个有趣的观察结果是,在本例中,
yield
实际上只是一个关键字,可以放在带有回调的函数之前

function* main() {
  console.log(yield function(cb) { cb(null, "Hello World") })
}
将打印“Hello World”。因此,您实际上可以通过创建相同的函数签名(不带cb)并返回
函数(cb){}
,将任何回调函数转换为使用
yield
,如下所示:

function yieldAsyncFunc(arg1, arg2) {
  return function (cb) {
    realAsyncFunc(arg1, arg2, cb)
  }
}
希望有了这些知识,您可以编写更干净、更可读的代码

非常简单,这就是它的工作原理

  • 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');
}
让_进程=进程()

在调用\u进程之前。next()它不会执行前两行代码,然后第一个yield将暂停该函数。 要继续函数直到下一个暂停点(产生关键字),您需要调用\u process.next()

您可以认为,多个产量是单个函数中javascript调试器中的断点。直到 您告诉导航下一个断点,它不会执行代码 块(注意:不阻止整个应用程序)

但是,当yield执行这种暂停和恢复行为时,它也可以返回一些结果
{value:any,done:boolean}
根据前面的函数,我们没有发出任何值。如果我们浏览前面的输出,它将显示相同的
{value:undefined,done:false}
未定义

让我们埋头干吧
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];
yield any;
yield {age: 12};
let val = yield 99; 

_process.next(10);
now the val will be 10 
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 
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}
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+++++++++++");
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+++++++++++");
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);
}
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