JS异步编程,你不得不知道的Generator的用法

timg.jpg

前面的文章中为大家讲解了Promise、async/await、Event Loop 等关于JS异步的文章,其中,关于async/await,我们知道,它是基于Generator的一个语法糖,而其返回的又是一个Promise,对于Promise我们已经不再陌生,并且它有着很实际的开发应用。那么关于Generator,今天小编就来带着大家简单了解一下。本人觉得Generator知识点和API繁多,对于一般的开发者来说,只需要掌握其中几个重要的概念和API就可以。本文主要讲解yieldnext,这也是Generator中最重要的两个概念,对于以后对其他基于Generator封装的轮子的学习和Generator的其他知识扩展有更大帮助和必要。

什么是Generator?

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。

首先,我们看一下Generator函数的语法,看一下究竟是哪里与传统函数完全不同。

形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

我们根据上面的描述创建一个Generator函数

function* helloGenerator(){
    yield 'hello'
    console.log('我是一个普通表达式')
    yield 'Generator'
    return '执行完毕'
}

Generator函数的调用

我们按照规则定义了一个Generator函数,现在我们调用它,Generator函数的调用方式和普通函数相同

var hg = helloGenerator()
console.log(hg,'helloGenerator调用结果')

我们在控制台打印我们调用的结果如下


捕获.PNG

我们发现,Generator函数并不能像普通函数那样直接返回结果,如果是普通函数,我们在调用赋值的时候,就可以拿到函数调用的结果,这就Generator函数的不同。
按照官方文档的介绍,我们应该这样调用它

hg.next()
//为了方便看执行结果,我们打印一下
console.log(hg.next())

我们看一下执行的结果

捕获2.PNG

我们看到了通过next调用后Generator函数的返回值,它是一个对象,其中value是我们yield后面的值,另外还有一个done属性,它是什么呢?我们发现通过next调用后的Generator函数并没有一下执行完,而是返回了第一个yield后面的结果,下面我们接上面代码再用next调用一次

console.log(hg.next())//上一次调用
console.log(hg.next())

我们看下执行结果

捕获3.PNG

我们看到,我们第二次通过next调用,函数接着上一次往下执行,并且有一次停在了yield的地方。那么我们可以猜想,yield就像是让函数暂停了,而next则让函数结束暂停状态接着向下执行,直到下一个‘断点’。
为了验证我们的猜想,我们接着使用next来调用。

console.log(hg.next())
console.log(hg.next())
console.log(hg.next())
console.log(hg.next())
console.log(hg.next())
console.log(hg.next())

我们用多个next来调用,下面我们看一下结果

捕获4.PNG

我们发现,结果是符合我们上面的猜想的,并且,当执行到return的时候,done属性的值就变成了true。并且 以后所有的执行结果中value属性都是undefined,done属性都是true
于是我们得到了下面的结论

Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。

调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

文章到这里,介绍了Generator最基本且必要的两个核心概念。到这里我们知道了,我们可用Generator来把函数分段执行。也可以利用其和普通函数调用方法不一样的特点,写一些特殊的扩展功能,下面是一个暂缓执行的函数的例子

function* f() {
  console.log('执行了!')
}

var generator = f();

setTimeout(function () {
  generator.next()
}, 2000);

上面的函数中,我们并没有使用yield,如果是普通函数,我们在调用赋值阶段就已经执行了。而我们用到generator函数就会等next来调用它。
关于yieldnext的使用注意事项,下面简单罗列几项

  • yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行
function* gen() {
  yield  123 + 456;
}

上面代码中,yield后面的表达式123 + 456,不会立即求值,只会在next方法将指针移到这一句时,才会求值。

  • yield表达式与return语句既有相似之处,也有区别。相似之处在于,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield表达式。正常函数只能返回一个值,因为只能执行一次return;Generator 函数可以返回一系列的值,因为可以有任意多个yield。如果函数中没有yield只有return,那么函数就会一直执行到return
  • yield表达式只能用在 Generator 函数里面,用在其他地方都会报错。(自行验证)

文章到这里就结束了,本文章只是带大家验证和了解Generator最基本的只是,需要深入了解的同学,还需要好好专研。
本文参考阮一峰《ECMAScript 6 入门》
谢谢您的阅读

你可能感兴趣的:(JS异步编程,你不得不知道的Generator的用法)