码迷,mamicode.com
首页 > 其他好文 > 详细

ES6生成器基础

时间:2015-02-09 21:35:12      阅读:325      评论:0      收藏:0      [点我收藏+]

标签:

    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生成器函数在并发中是“合作”的。在生成器函数体内,可以使用新的yeild关键字从内部暂停自己。没有什么可以从外部暂停一个生成器,函数在内部遇到yeild时可以暂停自己。

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

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

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

语法!

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

    首先,新的声明语法:

function *foo() {
    // ..
}

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

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

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

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

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

    在任何表达式的位置,在表达式或声明中可以单独使用yeild。yeild有一个假定的值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();

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

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

 

ES6生成器基础

标签:

原文地址:http://www.cnblogs.com/linda586586/p/4282359.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!