Javascript 生成器:理解它们 [英] Javascript Generators: Understanding them

查看:30
本文介绍了Javascript 生成器:理解它们的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很确定我对生成器的理解本质上是错误的.所有在线资源似乎都存在冲突,这会导致非常困难和令人困惑的学习体验.

据我所知,yield 关键字使当前正在执行的代码块能够等待一个值,而不是抛出要在回调中执行的剩余代码.所以,正如大多数教程所指出的,你可以使用这个:

(function *() {//等到用户被获取并放入 `results` 的值中var 结果 = yield db.get("users");//然后继续视图.显示(结果);})();

代替:

db.get("user", function(results) {视图.显示(结果);});

好吧,在我尝试编写自己的生成器之前,这一切都很好.我遇到了几个问题:

  • 我上面的第一个示例代码不会运行,因为没有任何东西可以迭代生成器,对吗?某些更高的存在必须在某处调用 .next ,对吗?
  • 整个 API 都必须重写到 I/O 调用以支持生成器,对吗?
  • 据我所知,yield 似乎代表 wait for the value 最一般的用例,而在实现部分(阅读:返回值到/inside db.get) yield 似乎代表 将此值发送回当前等待的块以恢复执行.

举个例子:

function *fn() {产量 1;产量a";}var gen = fn();gen.next();//1gen.next();//一种";

yield 在该上下文中将值发送回而不是等待结果.在上面的第一个示例中,它等待 db.get 的结果并恢复执行,而不是返回"或发回一个值.如果 db.get 情况为真,这不是天生同步的吗?我的意思是,这不是完全一样的:

(function() {//等待结果var 结果 = fs.readFileSync("users.txt");//使用结果视图.显示(结果);})();

不幸的是,如果从这个问题中可以清楚地看出(可能唯一清楚的事情)就是我不理解生成器.希望我能在这里得到一些见解.

解决方案

TL;DR:生成器的本质是控制代码执行的暂停.

关于生成器本身,你可以参考这个.>

总结起来,你应该区分三个组件:1. 发电机功能2. 发电机3. 生成结果

生成器函数只是头部带有星号和(可选)yield 主体的function.

function *generator() {console.log('开始!');变量 i = 0;而(真){如果 (i <3)产量 i++;}}var gen = 生成器();//这里什么也没发生!!

Generator 函数本身不做任何事情,只返回一个生成器,在上面的例子中,gen.这里没有控制台输出,因为只有在调用返回的 generatornext 方法后,generator 函数 的主体才会运行.Generator 有多种方法,其中最重要的是next.next 运行代码并返回生成器结果.

var ret = gen.next();//开始!控制台日志(ret);//{值:0,完成:假}

ret 上面是生成器结果.它有两个属性:value,你在 generator function 中产生的值,以及 done,一个标志,指示 generator function 返回.

console.log(gen.next());//{值:1,完成:假}console.log(gen.next());//{值:2,完成:假}console.log(gen.next());//{值:未定义,完成:真}

此时,没有人会期望你了解generator,至少不是generator的异步能力.

简单来说,生成器有两个特点:

  • 可以选择跳出函数,让外部代码决定何时跳回函数.
  • 异步调用的控制可以在你的代码之外完成

在代码中,yield 跳转到函数外,next(val) 跳转回函数并将值传回函数.外部代码可以处理异步调用并决定适当的时间切换到您自己的代码.

再看样本:

var gen = generator();console.log('生成的生成器');console.log(gen.next().value);//模拟 long long 处理设置超时(函数(){console.log(gen.next().value);console.log('定时器触发后执行');}, 1000);console.log('定时器设置后执行');/* 结果:生成器开始0定时器设置后执行1定时器触发后执行*/

看到了吗?生成器函数本身不处理回调.外部代码可以.

基地就在这里.您可以详细说明此代码以支持完全异步,同时保持生成器功能,如同步.

例如,假设 geturl 是一个返回 promise 对象的异步调用.你可以写 var html = yield getUrl('www.stackoverflow.com'); 这会跳到你的代码之外.外部代码将执行以下操作:

var ret = gen.next();ret.then(function (fetchedHTML) {//跳回到你的生成器函数//并在您的代码中将 fetchHTML 分配给 htmlgen.next(fetchedHTML);});

更完整的指南,请参考这个.以及像 cogalaxy暂停

I'm pretty sure my understanding of generators is inherently broken. All online resources seem to conflict and it makes for an incredibly difficult and confusing learning experience.

From what I understand, the yield keyword enables a currently executing block of code to wait for a value instead of throwing remaining code to be executed inside a callback. So, as most tutorials have pointed out, you can use this:

(function *() {
     // Wait until users have be got and put into value of `results`
     var results = yield db.get("users");
     // And continue
     view.display(results);
})();

Instead of:

db.get("user", function(results) {
    view.display(results);
});

Right, that's all well and good until I try to write my own generators. I've run into several hitches:

  • The first example code I above will not run because there is nothing to iterate over the generator, correct? Some higher being has to call the .next somewhere, right?
  • The entire API will have to be rewritten right down to the I/O calls to support generators, correct?
  • From what I gather, yield seems to stand for wait for the value most general use cases whereas in the implementation part (read: return value to/inside db.get) yield seems to stand for send this value back to currently waiting block to resume execution.

Take for example:

function *fn() {
    yield 1;
    yield "a";
}

var gen = fn();
gen.next(); // 1
gen.next(); // "a";

yield in that context is sending values back down instead of waiting for the results. In the first example above, it waits for the results from the db.get and the resumes execution instead of "returning" or sending back a value. If the db.get case is true, is this not inherently synchronous? I mean, isn't it exactly the same as:

(function() {
     //Wait for the results
    var results = fs.readFileSync("users.txt");
    // Use results
    view.display(results);
})();

Unfortunately, if it's any way clear from this question (probably the only thing clear) is that I don't understand generators. Hopefully, I might get some insight here.

解决方案

TL;DR: the essence of generator is controlling the suspension of code execution.

For generator itself, you can refer to this.

To sum up, there is three components you should distinguish: 1. generator function 2. generator 3. generated result

Generator function is simply the function with star in its head and (optional) yield in its body.

function *generator() {
  console.log('Start!');
  var i = 0;
  while (true) {
    if (i < 3)
      yield i++;
  }
}

var gen = generator();
// nothing happens here!!

Generator function itself does not do anything but return a generator, in the case above, gen. No console output here because only after the returned generator's next method is called the body of generator function will run. Generator has several methods, of which the most important one is next. next runs the code and returns the generator result.

var ret = gen.next();
// Start!
console.log(ret);
// {value: 0, done: false}

ret above is generator result. It has two property: value, the value you yield in generator function, and done, a flag indicating whether the generator function return.

console.log(gen.next());
// {value: 1, done: false}
console.log(gen.next());
// {value: 2, done: false}
console.log(gen.next());
// {value: undefined, done: true}

At this point, no one will expect you to understand generator, at least not the async power of generator.

To put it simple, generator has two features:

  • one can choose to jump out of a function and let outer code to determine when to jump back into the function.
  • the control of asynchronous call can be done outside of your code

In code, yield jumps outside of function, and next(val) jumps back to the function and pass value back into the function. Outside code can handle asynchronous call and decide proper time to switch to your own code.

Look the sample again:

var gen = generator();
console.log('generated generator');
console.log(gen.next().value);
// mock long long processing
setTimeout(function() {
  console.log(gen.next().value);
  console.log('Execute after timer fire');
}, 1000);
console.log('Execute after timer set');

/* result:
    generated generator
    start
    0
    Execute after timer set
    1
    Execute after timer fire
*/

See? The generator function itself does not handle callback. The outside code does.

The base is here. You can elaborate this code to support full asynchronousity while keeping generator function like sync one.

For example, suppose geturl is an asynchronous call that returns a promise object. you can write var html = yield getUrl('www.stackoverflow.com'); This jumps outside your code. And outside code will do stuff like:

var ret = gen.next();
ret.then(function (fetchedHTML) {
  // jumps back to your generator function
  // and assign fetchHTML to html in your code
  gen.next(fetchedHTML);
});

For more complete guide, refer to this. And repository like co, galaxy, suspend and etc.

这篇关于Javascript 生成器:理解它们的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆