ES6的generator函数

generator是什么?

generator是ES6提供的一种异步编程解决方案,在语法上,能够把它理解为一个状态机,内部封装了多种状态。执行generator,会生成返回一个遍历器对象。返回的遍历器对象,能够依次遍历generator函数的每个状态。同时ES6规定这个遍历器是Generator函数的实例,也继承了Genarator函数的prototype对象上的方法。编程

最简单的generator函数,其实它就是一个普通的函数,可是它有两个特征。第一就是function关键字与函数名之间有一个*号,其二就是函数体内使用yield表达式来遍历状态:promise

function* newGenerator(){
    yield 'hello';
    yield 'world';
    return 'ending';
}

执行generator函数以后,该函数并不会当即执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。一般使用遍历器对象的next方法。使得指针移向下一个状态。每一次调用next()方法,内部指针就从函数头部或上一次停下里的地方开始执行,直到遇到下一个yield表达式位置,由此能够看出,generator是分段执行的,yield表达式是暂停执行的标记,而next方法能够恢复执行。数据结构

generator中的yield表达式

yield表达式在generator中是做为一个暂停标志,当碰到yield时,函数暂停执行,等到下一次next()执行时,函数才从当前yield位置开始执行。而且,yield表达式只能用在Generator函数里边;同时,yield若是后边带一个,则就是至关于一个for...of的简写形式,若是yield后边不带,则返回的是generator的值。多线程

function* gen() {
    yield 'hello';
    yield* 'hello';
}
let f = gen();
console.log(f.next().value);
console.log(f.next().value);
console.log(f.next().value);
console.log(f.next().value);
console.log(f.next().value);

上述例子中的后四个next()函数,就会顺序的返回h e l l异步

generator中的next函数

经过next函数,能够执行对应的yield表达式,且next()函数还能够带参数,该参数能够做为上一次yield表达式的返回值,由于yield自己是没有返回值的,若是next()中不带参数,则yield每次运行以后的返回值都是为undefined;async

function* dataConsumer() {
    console.log('Started');
    console.log(`1. ${yield}`);
    console.log(`2. ${yield}`);
    return 'result';
}

let genObj = dataConsumer();
genObj.next();
// Started
genObj.next('a');
// 1. a
genObj.next('b');
// 2. b

上述函数中,第一次运行next(),运行到第一个next()函数截止,第二个next运行时,传入的参数为'a';则运行到第二个yield地方截止,而后第一个yield运行的返回值为'a',依次类推,则获得上述结果。编辑器

另外,经过for...of能够循环generator中的全部状态,而且不须要使用next()函数。除了for...of循环之外,扩展运算符(...),解构赋值和Array.form方法内部调用的,都是遍历器接口。异步编程

generator生成的对象,还有其余一些函数,好比throw()用来抛出错误,return()用来定义返回值并终止generator的状态。函数

以上的三个方法在本质上实际上是同样的,他们就是让generator恢复执行,而且使用不一样的语句来替代yield语句。lua

  • next()是将yield表达式替换成一个值
  • throw()是将yield表达式替换成一个throw语句
  • return()是将yield表达式替换成一个return语句

Generator与协程

协程能够理解为“协做的线程”或者“协做的函数”。协程既能够是单线程实现,也能够用多线程实现,前者是一种特殊的子例程,后者是一种特殊的线程。

协程有点像函数,又有点像线程,它的运行流程大体以下。

  • 第一步,协程A开始执行
  • 第二部,协程A执行到一半,进入暂停,执行权转移到协程B
  • 第三步,(过了一段时间)协程B交换执行权
  • 最后,协程A恢复执行

协程适合用于多任务运行环境,它与普通的线程很类似,都有本身的执行上下文,能够分享全局量。他们的不一样之处在于,同一时间能够有多个线程处于运行状态,可是运行的协程只能有一个,其余协程都是处于暂停状态。

因为JavaScript是单线程,只能保持一个调用栈,引入协程以后,每个任务能够保持本身的调用栈,这样就能够再抛出错误的时候找到原始的调用栈,不至于像异步操做的回调函数那样,一旦出错,原始的调用栈早就结束了。

Generator 函数是 ES6 对协程的实现,但属于不彻底实现。Generator函数被称为“半协程”(semi-coroutine),意思是只有 Generator 函数的调用者,才能将程序的执行权还给 Generator函数。若是是彻底执行的协程,任何函数均可以让暂停的协程继续执行。

若是将Generator函数当作协程,彻底能够将多个须要互相协做的任务写成Generator函数,他们之间使用yield标识交换控制权。

Generator 函数执行产生的上下文环境,一旦遇到yield命令,就会暂时退出堆栈,可是并不消失,里面的全部变量和对象会冻结在当前状态。等到对它执行next命令时,这个上下文环境又会从新加入调用栈,冻结的变量和对象恢复执行。

Generator函数的多种用途

  • 可使异步操做来实现同步化表达
  • 控制流管理
  • 部署Iterator接口
  • 作为数据结构

上述的介绍中,咱们看到了generator是什么,下边咱们看一下,目前中,咱们使用最多的,generator函数的异步调用。

异步编程对于单线程的JavaScript无疑是十分重要的(能够笼统的将异步定义为不连续的执行)。以前的文章中,咱们也说过,JavaScript中对于异步的实现,就是回调函数。咱们以前也有使用过promise去进行死亡回调的改良,promise来使回调嵌套的表现形式更好了些。其实呢generator函数也能够用来实现异步回调的嵌套。

整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。异步操做须要暂停的地方,都用yield语句注明。除此以外,它还有两个特性,使它能够做为异步编程的完整解决方案:函数体内外的数据交换和错误处理机制。即为next方法还能够接收参数,向Generator函数体内输入数据。

Thunk函数

Thunk函数是自动执行Generator函数的一种方法。

对于之前的函数参数的求值计算,有两种计算方式,一种是传值调用,一种是传名调用。编辑器中的“传名调用”的实现,每每是将参数放到一个临时函数之中,再将这个临时函数传入函数体,这个临时函数就叫作Thunk函数。

JavaScript中使用的是传值调用,它的Thunk函数含义有所不一样。再JavaScript中,替换的不是表达式,而是多参数函数的休整。

Thunk函数能够用于Generator函数的自动流程管理。即便Generator函数能够自动执行。

co模块

co模块用于Generator函数的自动执行。co函数返回一个Promise对象,所以能够用then方法添加回调函数。

使用了Generator函数以后,咱们能够将多个异步嵌套的代码改成同步写异步,大大的简化了咱们的代码量,以及代码的美观。

上边就是大概的ES中的Generator函数的介绍,咱们可使用Generator来实现 lazy evaluation,Iterator和实现异步调用同步的写法,可是其中咱们面临的更多的仍是generator的执行问题,下一篇咱们来看一下generator是怎么实现async/await来控制异步。以及co模块的实现。

相关文章
相关标签/搜索