ES6引进的最令人兴奋的特性就是一种新的函数生成方式,称为生成器(generator)。名称有点奇怪,但是第一眼看上去行为更加奇怪。文章主要介绍生成器如何工作,然后让你明白为什么他们对于未来的JS会有很大的影响。

完成运行

首先看看生成器和普通函数有什么不同。无论你是否已经意识到,关于你的函数,总是可以很基本的假设一些东西:一但函数开始运行,它总是在其他JS代码可以运行前运行完毕。例子:

setTimeout(function(){
console.log("Hello World");
},1); function foo() {
// NOTE: don't ever do crazy long-running loops like this
for (var i=0; i<=1E10; i++) {
console.log(i);
}
} foo();
// 0..1E10
// "Hello World"

在这里,完成for循环会花相当长的时间,比1毫秒长很多,但是我们的计时器回调函数console.log("Hello World")并不能在foo()函数运行期间打断他,所以它卡住了,排在最后面(在循环后面),耐心地等待轮到他。

但是如果foo()可以被打断是什么样的?难道不会对程序造成严重破坏吗?

这就是噩梦般的多线程编程挑战,但是很幸运,在JS领土中,不用担心这种情况,因为JS总是单线程的(在任何给定的时间内,只有一条命令\函数执行)。

注:Web开发人员对于一部分JS程序,可以作为一个独立的线程来运行,完全与主JS线程并行。这样不会引起多线程并行到程序中的理由,是两个线程只能通过普通异步事件和彼此交互,而异步事件遵循的是一次只执行一条的原则。

运行...停止...运行

通过ES6生成器,有了一种不同的函数,这种函数可以停在中间,一次或者多次,过后又恢复,允许其他代码在这个停止期运行。

如果阅读过关于并发或者多线程的任何东西,会看到“合作”期,即一个进程(一个函数)自己选择什么时候允许打断,以便它可以和其他代码“合作”。这一概念与"先发"形成对比,表明进程/函数可能违背其意愿被打断。

ES6生成器函数在并发中是“合作”的。在生成器函数体内,可以使用新的yield关键字从内部暂停自己。没有什么可以从外部暂停一个生成器,函数在内部遇到yield时可以暂停自己。

但是,生成器函数包含yield暂停自己的时候,它不能恢复自己。必须使用外部控制来重启生成器。后续会介绍这个怎么发生的。

所以,一个生成器函数可以停止,被重新启动,可以自定义次数。事实上,可以使用一个无限循环(类似while (true) { .. })来指定一个生成器函数。在平常的JS程序里面这可能是疯狂的或者是一个错误,对于生成器函数来说是很理智的,有时就是你想要做到的!

更重要的,这个停止和开始并不是在生成器函数执行中的一个控制,允许2通道消息传入和传出生成器。在平常的函数中,在开始可以获取到参数,在结尾处获得一个return值。利用生成器函数,使用每个yield向外输出消息,使用每个start回送消息。

语法!

让我们看看这个新的、令人兴奋的生成器函数的语法。

首先,新的声明语法:

function *foo() {
// ..
}

注意到这里的*了么?这就是全新的有点奇怪的格式。在其他的一些语言里面,看上去很像糟糕的函数返回指针。但是不要疑惑!这只是一个标记生成器函数的方式。
    你也许在其他文章里面看到过function* foo(){ } 来替代 function *foo(){ } (*的位置不同),这两种情况都是合法的,但是最近觉得function *foo() { }更明确一些,也就是这里用到的方式。

现在我们讨论一下生成器函数的内容。大多数情况下生成器函数只是普通的JS函数。在生成器函数内部基本没什么新的语法可学的。

我们主要必须玩会的新玩具,在上面提到过,就是yield关键字。yield__被称为"yield表达式"(不是声明)是因为当启动生成器的时候,我们会传递一个值进去,传递进去的值会是yield表达式的计算结果。例如:

function *foo() {
var x = 1 + (yield "foo");
console.log(x);
}

yield "foo"表达式会在暂停生成器函数的时候将foo值传出来,当生成器重新启动的时候,传递进来的值会是那个表达式的结果,会加1指给x.
    看到2通道的交互了么?你将"foo"传出,暂停了自己,在过后的某时间点(可能是立即,也可能是距离现在很长的一段时间),生成器函数会重新启动会传递一个回送值。

在任何表达式的位置,在表达式或声明中可以单独使用yield。yield有一个假定的值undefined:

// note: `foo(..)` here is NOT a generator!!
function foo(x) {
console.log("x: " + x);
} function *bar() {
yield; // just pause
foo( yield ); // pause waiting for a parameter to pass into `foo(..)`
}

生成器迭代器

迭代器是一种特殊的用法,事实上是一种设计模式,当使用next()进入一系列预选的值中的时候,想象下在一个有5个值的数组上使用迭代器的时候,第一个next()调用会返回1,第二个next()调用会返回2,以此类推。等到所有值被返回,next()会返回null或false或者其他标志位来表示你已经迭代完了所有的值。

从外部控制生成器函数的方法是使用生成器迭代器构建和交互。听起来比实际复杂些。例子:

function *foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
}

进入*foo()生成器的值中,需要构建一个迭代器:

var it = foo();

所以使用平常的方法调用生成器函数不会执行它的任何一部分。

这看起来有点奇怪。你也许想要知道,为什么不是var it = new foo()。这语法后的原因很复杂已经超出了这里的讨论范围。

所以,现在开始迭代我们的生成器函数,我们只需:

var message = it.next();

这会从yield 1声明返回1,但那不是我们仅仅得到的。

console.log(message); // { value:1, done:false }

实际上我们从每个next()调用得到了一个对象,它有一个yield传出值的value属性,done是一个表示生成器函数是否运行完成的布尔变量。

继续看迭代器:

console.log( it.next() ); // { value:2, done:false }
console.log( it.next() ); // { value:3, done:false }
console.log( it.next() ); // { value:4, done:false }
console.log( it.next() ); // { value:5, done:false }

有趣的是,done在我们得到5这个值的时候仍然是false。这是因为,技术上,生成器函数没有完成。我们不得不调用最后一个next()调用,如果我们传入一个值,它必须作为yield5表达式的结果。只有那时生成器函数完成了。
    所以,现在:

console.log( it.next() ); // { value:undefined, done:true }

生成器函数的最后的结果是我们调用完成了,但是没有结果给出来。
    你也许想知道,在生成器函数内可以用return么,如果用了,那个值会在value属性里面输出么?

是的...

function *foo() {
yield 1;
return 2;
} var it = foo(); console.log( it.next() ); // { value:1, done:false }
console.log( it.next() ); // { value:2, done:true }

不是...

在生成器内依赖return不是一个好的做法,因为使用for-of循环迭代生成器的时候,最后return的值会被忽略。

为降低风险,我们迭代生成器的时候看看传递进去和出来信息:

function *foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
} var it = foo( 5 ); // note: not sending anything into `next()` here
console.log( it.next() ); // { value:6, done:false }
console.log( it.next( 12 ) ); // { value:8, done:false }
console.log( it.next( 13 ) ); // { value:42, done:true }

可以看到,仍然可以通过内部的foo(5)迭代器实例化调用向内传递参数,就像普通函数一样,使x为5.
    第一个next()调用,我们没有传递任何参数。为什么?因为没有yield表达式来接收传递的参数。

英文原文:http://davidwalsh.name/es6-generators

ES6生成器基础的更多相关文章

  1. 深入探讨ES6生成器

    如果对于ES6生成器不熟悉,请先阅读并运行下http://www.cnblogs.com/linda586586/p/4282359.html里面的代码.当你感觉掌握了基础之后,我们可以深入探讨一些细 ...

  2. 学习ES6生成器(Generator)

    背景 在JS的使用场景中,异步操作的处理是一个不可回避的问题,如果不做任何抽象.组织,只是“跟着感觉走”,那么面对“按顺序发起3个ajax请求”的需求,很容易就能写出如下代码(假设已引入jQuery) ...

  3. 【翻译】ES6生成器简介

    原文地址:http://davidwalsh.name/es6-generators ES6生成器全部文章: The Basics Of ES6 Generators Diving Deeper Wi ...

  4. ES6生成器函数generator

    ES6生成器函数generator generator是ES6新增的一个特殊函数,通过 function* 声明,函数体内通过 yield 来指明函数的暂停点,该函数返回一个迭代器,并且函数执行到 y ...

  5. es6常用基础合集

    es6常用基础合集 在实际开发中,ES6已经非常普及了.掌握ES6的知识变成了一种必须.尽管我们在使用时仍然需要经过babel编译. ES6彻底改变了前端的编码风格,可以说对于前端的影响非常巨大.值得 ...

  6. es6属性基础教学,30分钟包会

    ES6基础智商划重点在实际开发中,ES6已经非常普及了.掌握ES6的知识变成了一种必须.尽管我们在使用时仍然需要经过babel编译.ES6彻底改变了前端的编码风格,可以说对于前端的影响非常巨大.值得高 ...

  7. 小白学 Python(21):生成器基础

    人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 小白学 Python(4):变 ...

  8. ES6生成器与迭代器

    ES6迭代器的一个例子 function run(taskDef) { var task = taskDef(); var result = task.next(); // 递归执行迭代 functi ...

  9. ES6 的基础教程

    一.介绍 1.历史 ECMAScript和JavaScript ECMA是标准,JS是实现 类似于HTML5是标准,IE10.Chrome.FF都是实现 换句话说,将来也能有其他XXXScript来实 ...

随机推荐

  1. ARM NEON编程系列1-导论

    ARM NEON 编程系列1 - 导论 前言 本系列博文用于介绍ARM CPU下NEON指令优化. 博文github地址:github 相关代码github地址:github NEON历史 ARM处理 ...

  2. ADF_Starting系列4_使用ADF开发富Web应用程序之维护User Interface(Part1)

    2014-05-04 Created By BaoXinjian

  3. RAC_Oracle集群服务安装RAC(案例)

    2015-01-23 Created By BaoXinjian Thanks and Regards

  4. PLSQL_闪回操作4_Flashback Drop

    2014-06-25 Created By BaoXinjian

  5. 实现读入一个彩色视频文件并以灰度格式输出这个视频文件,学习opencv例2-10

    #include "cv.h"#include "highgui.h"int main(int argc,char* argv[]){ //书本中的main没有 ...

  6. vim使用快捷键

    vim使用快捷键 索引 1. 关于Vim 1.1 Vim的几种模式 2. 启动Vim 3. 文档操作 4. 光标的移动 4.1 基本移动 4.2 翻屏 4.3 标记 5. 插入文本 5.1 基本插入 ...

  7. JAVA if条件语句,switch多分支结构,与逻辑运算符

    一.if条件语句 示例: import java.util.Scanner; public class Test{ public static void main(String[] args){ Sc ...

  8. 移动设备wap手机网页html5通过特殊链接:打电话,发短信,发邮件详细教程

    如果需要在移动浏览器中实现拨打电话,调用sms发短信,发送email等功能,移动手机WEB页面(HTML5)Javascript提供的接口是一个好办法. 采用url href链接的方式,实现在Safa ...

  9. ajax实现的无刷新分页代码实例

    一.html代码部分: <table class="table style-5">   <thead id="t_head">     ...

  10. Bugtags:移动时代首选 Bug 管理系统

    Bug 管理系统之重 回想我们每次开启一个新项目,筹备之初,首要之事就是选择一款 Bug 管理系统.市面上有诸多 Bug 管理系统可供选择:Jira.Redmine.Bugzilla 等.这些系统功能 ...