我们先看es6里的是这样子:

//Generator函数
function* chain(){
    yield 'a';
    console.log('sss');
    yield 'b';
    yield 'c';
    return 'end';
}
var exp=chain()
console.log(typeof(exp))
//value是yield后面的值,done表示的是当前函数是否执行完毕
console.log(exp.next())//{value: "a", done: false}
console.log("附件打开");
// 注意,执行b的next的时候,才打印sss
console.log(exp.next())//{value: "b", done: false}
console.log(exp.next())//{value: "c", done: false}
console.log(exp.next())//{value: "end", done: true}

然后生成es5是这样:

'use strict';

var _marked = /*#__PURE__*/regeneratorRuntime.mark(chain);

//Generator函数
function chain() {
    return regeneratorRuntime.wrap(function chain$(_context) {
        while (1) {
            switch (_context.prev = _context.next) {
                case 0:
                    _context.next = 2;
                    return 'a';

                case 2:
                    console.log('sss');
                    _context.next = 5;
                    return 'b';

                case 5:
                    _context.next = 7;
                    return 'c';

                case 7:
                    return _context.abrupt('return', 'end');

                case 8:
                case 'end':
                    return _context.stop();
            }
        }
    }, _marked, this);
}
var exp = chain();
//value是yield后面的值,done表示的是当前函数是否执行完毕
console.log(exp.next()); //{value: "a", done: false}
console.log("附件打开");
// 注意,执行b的next的时候,才打印sss
console.log(exp.next()); //{value: "b", done: false}
console.log(exp.next()); //{value: "c", done: false}
console.log(exp.next()); //{value: "end", done: true}

可以看到,编译器/运行时会对yield进行转换/打洞,将每个yield拆分到多个块里(yield行和之上行的代码[不包括上一次yield]会作为一个计算块);

这里其实regeneratorRuntime.wrap(…)返回的就类似C# IEnumerable<Object>一样的状态机/迭代器/生成器;

里面有context对象,context对象又记录了prev和next两个”游标”,当执行next的时候(C#里的for xx in iter本质上也是执行了iter的next())会将自己的context作为参数调用chain$,然后根据游标值来看这次next是要执行哪个计算块;

 

然后是C#的代码,无yield:

// See https://aka.ms/new-console-template for more information

IEnumerable<int> fibonaccis = Fibonacci(4);
foreach (var f in fibonaccis)
{
    Console.Write("{0} ", f);
}
//计算斐波拉契数据
IEnumerable<int> Fibonacci(int count)
{
  int p= 1;
  int c= 1;
  List<int> result = new List<int>();
  for (int i = 0; i < count; i++)
  {
    result.Add(p);
    Thread.Sleep(2000);
    int temp = p+ c;
    p= c;
    c= temp;
  }
  return result;
}

// yield: 1 1 2 3 Hello, World!Program+<<<Main>$>g__Fibonacci|0_0>d
// 无yield: 1 1 2 3 Hello, World!System.Collections.Generic.List`1[System.Int32]
Console.WriteLine("Hello, World!" + fibonaccis.GetType());

有yield:

// See https://aka.ms/new-console-template for more information

IEnumerable<int> fibonaccis = Fibonacci(4);
foreach (var f in fibonaccis)  // 每次in赋值给f时,都是执行了类似next获取下一个元素的方法,而这个next的方法就类似es5里的chain$
{
    Console.Write("{0} ", f);
}
//计算斐波拉契数据
IEnumerable<int> Fibonacci(int count)
{
  int p= 1;
  int c= 1;
  for (int i = 0; i < count; i++)
  {
    yield return p;
    Thread.Sleep(2000);
    int temp = p+ c;
    p= c;
    c= temp;
  }
}

// yield: 1 1 2 3 Hello, World!Program+<<<Main>$>g__Fibonacci|0_0>d
// 无yield: 1 1 2 3 Hello, World!System.Collections.Generic.List`1[System.Int32]
Console.WriteLine("Hello, World!" + fibonaccis.GetType());

可以看出有yield和无yield的IEnumerable<int>的实现类是不一样的,必须的不一样,因为fibonaccis提取下一个元素的方法(假设叫next())next在非yield里其实就类似从集合里取值,而yield的next方法则是内部又调用了一个类似es5里的chain$的方法来取值(会传context给chain$),即两种得到IEnumerable<int>对象的取值逻辑都不一样,自然其实现类是不会一样的

原文地址:http://www.cnblogs.com/silentdoer/p/16864326.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性