Javascript 1.7中的Yield
发布于 12 年前 作者 zealot 22470 次浏览 最后一次编辑是 8 年前

先从coroutine说起 <br/> <br/>coroutine是一种常见的流程控制语句, 可以在单线程内模拟多线程的执行, 经常被用于非抢占式用户态纤程(Fiber) <br/>这里先要区分一下两种不同的coroutine <br/><ul> <br/> <li>stackless coroutine (semi-coroutine)</li> <br/> <li>stackful coroutine (coroutine)</li> <br/></ul> <br/>stackless coroutine又经常被称为generator. 下面这些语言中的<span style=“color: blue;”>yield</span>都属于stackless coroutine <br/><ul> <br/> <li><strong>javascript 1.7</strong></li> <br/> <li>python</li> <br/> <li>ruby</li> <br/></ul> <br/>本文主要是讲解yield的本质及其实现原理, 对于yield比较陌生的同学可以先简单学习一下javascript 1.7的文档: <a href=“https://developer.mozilla.org/en/New_in_JavaScript_1.7#Generators”>https://developer.mozilla.org/en/New_in_JavaScript_1.7#Generators</a> <br/> <br/>stackless coroutine本质上仅仅是一个<strong>语法糖</strong>, 例如下面这段javascript代码, 可以转换成不使用yield的代码: <br/><pre>function fib() { <br/> var i = 0, j = 1; <br/> while (true) { <br/> yield i; <br/> var t = i; <br/> i = j; <br/> j += t; <br/> } <br/>}</pre> <br/>首先把while/for循环转换成递归, 并且新建一个generator对象: <br/><pre>function fib() { <br/> var generator = {}; <br/> generator.next = function () { <br/> var i = 0, j = 1; <br/> var $while = function () { <br/> yield i; <br/> var t = i; <br/> i = j; <br/> j += t; <br/> $while(); <br/> /* 1 / <br/> } <br/> $while(); <br/> / 2 / <br/> } <br/> return generator; <br/>}</pre> <br/>然后对generator内部做cps变换. 经过cps变换后, yield成为一个普通函数: <br/><pre>function fib() { <br/> var generator = {}; <br/> var $yield = function (k, value) { <br/> generator.next = k; <br/> return value; <br/> }; <br/> generator.next = function () { <br/> var i = 0, j = 1; <br/> var $while = function (k / $while never return, so k is never called. /) { <br/> return $yield(function () { <br/> // all statements after yield become continuation of yield, which comes into this function <br/> var t = i; <br/> i = j; <br/> j += t; <br/> return $while(function () { / 1 / }); <br/> }, i / arguments of yield /); <br/> }; <br/> return $while(function () { / 2 */ }); <br/> } <br/> return generator; <br/>} <br/> <br/>// test code, copy into any javascript engine and check result <br/>var g = fib(); <br/>for (var i = 0; i < 10; i++) { <br/> console.log(g.next()); <br/>}</pre> <br/>上面所使用到了两种转换: <br/><ul> <br/> <li>循环变递归</li> <br/> <li>CPS变换</li> <br/></ul> <br/>这两种转换都是通用转换, 任何程序都可以做这样的转换, 通过这两种转换可以把任何使用yield的代码转换成普通代码. 实际上, stackless coroutine的实现就是编译器(或者解释器)在背后做了这些工作.

4 回复

在别的语言,协程的方式是很顺手的。 <br/> <br/>但是在js中,callback已经深入人心,我个人还是觉得回调和事件方式比较顺手。

这是采集的么…

为什么我看到一堆的<br/>??

转义模块被改了?

2011 年的时候有的还不是 Markdown, 早期的帖子都是有问题的… 管理员用空的时候手动一下吧

回到顶部