2021-02-08 ES6中的Generator函数的基本使用
异步编程方法:
- 回调函数
- 事件监听
- 发布/订阅
- Promise对象
- ES6的Generator函数异步应用
异步的概念:
简单来说就是一个任务不是连续完成的,而是分为两段执行,先执行第一段,转而去执行其他任务,等做好了准备,在回过头来执行第二段
Generator函数是一个状态机,封装了多个内部状态,执行Generator函数会返回一个遍历器对象,可以依次遍历Generator函数内部的每一个状态
Generator函数是一个普通函数,但是有两个特征:
1、function关键字与函数名之间有个星号
2、函数体内部使用yield表达式,定义不同的内部状态
调用Generator函数,该函数不执行,返回的不是函数运行结果而是一个指向内部状态的指针对象,下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态
也就是说,每次调用next方法,内容指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式或return语句未知
换言之,Generator函数是分段执行的,yield表达式是暂停执行的标记,next方法可以恢复执行,next方法返回的是一个对象{value,done}
,其中value是yield表达式或return返回的值,done表示遍历是否结束,遇到return是true
yield表达式
Generator函数返回的是一个遍历器对象,只有调用next方法才会遍历下一个内部状态,提供了yield可以暂停执行的标志
遍历器对象的next方法运行逻辑如下:
1)遇到yield
表达式,就暂停执行后面的操作吗,并将紧跟yield
后面的那个表达式的值,作为返回的对象的value
属性值
2)下一次调用next
方法,再继续往下执行,直到遇到下一个yield
表达式
3)如果没有遇到yield
表达式,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值,作为返回的对象的value
的值
4)如果该函数没有return
语句,则返回的对象的value
属性值为undefined
yield表达式与return语句既有相似之处也有区别。
相似之处在于都能返回紧跟在语句后面的那个表达式的值。
区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备记忆的功能
注:yield表达式本身并没有返回值,或者说总是返回undefined
next方法的参数
yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被上一个yield表达式的返回值
这个功能有很重要的意义,Generator函数从暂停状态到回复状态,它的上下文状态(context)是不变的。通过next方法的参数,就有办法在Generator函数开始运行之后,继续向内部注入值,也就是说,可以在Generator函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数的行为
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
注意:由于next方法的参数是表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数无效的。V8引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的
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,就能够输入值,可以在Generator函数外面在包一层
function wrapper(generatorFunction) {
return function (...args) {
let generatorObject = generatorFunction(...args);
generatorObject.next();
return generatorObject;
};
}
const wrapped = wrapper(function* () {
console.log(`First input: ${yield}`);
return 'DONE';
});
wrapped().next('hello!')
// First input: hello!
实际上是调用第二次的next函数