Generator函数概念
Generator
函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。
对于Generator
函数是有多重理解角度。从语法上,首先可以把它理解成一个状态机,封装了很多个内部状态。
执行
Generator
函数会返回一个遍历器对象,可以一次遍历Generator
函数内部的每一个状态。
Generator函数特征
- function命令与函数名之间有一个星号(
*
);- 函数体内部使用
yield
语句定义不同的内部状态(yield
是产出的意思);- 函数返回的是一个遍历器对象,须调用
next
方法才会一次返回不同状态的值。
function *helloWorldGenerator(){
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
上面代码就定义了一个Generator
函数--helloWorldGenerator,它内部有两个yield
语句,和一个return
语句,即该函数有三个状态:hello
、 world
、和 return
语句(结束执行);
与其他不同函数不同的是,Generator
函数调用之后,该函数并不会执行,返回也不是函数的运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象Iterator
,在之后,我们必须调用遍历器对象的next
方法,使得指正指向下一个状态,每次调用next
方法,内部指针就从函数的头部或者上一次停下来的地方开始执行,直到遇到下一条yield
语句,或者return
语句为止。
hw.next();
//{value: "hello", done: false}
hw.next();
//{value: "world", done: false}
hw.next();
//{value: "ending", done: true}
hw.next();
//{value: undefined, done: true}
上面一共执行了四次next
方法;
yield语句
由于Generator
函数返回的遍历器对象只有调用next
方法才会遍历下一个内部状态,所以提供了一种可以暂停执行的函数。yield
语句就是暂停标志。
yield
语句,与next
方法的运行逻辑
- 遇到
yield
语句就暂停执行后面的操作,并将紧跟在yield
后的表达式的值作为返回的对象的value
属性值。- 下一次调用
next
方法时再继续往下执行,直到遇到下一条yield
语句。- 如果没有遇到
yield
语句,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值作为返回对象的value
属性的值。- 如果该函数没有
return
语句,则返回对象的value属性值为undefined
,对象的done
属性值为true
,表示遍历已经结束。
-
yield
语句和return
语句的区别。
yield
语句和return
语句的相似之处在于都能返回紧跟在语句后的表达式的值。区别在于每次遇到yield
语句函数会暂停执行,下一次会从该位置继续向后执行,而人团语句不具备位置记忆的功能,一个函数里只能执行一次(或者说一条)return
语句,但可以执行多次yield
语句。
-
Generator
函数不用yield
语句。
这时候就变成了一个单纯的暂缓执行函数。
function *noyield(){
console.log('执行了!')
}
var generator = noyield();
generator.next();
//执行了!
- 普通函数中不能使用
yield
语句
function fn(){
yield 1;
}
fn() // SyntaxError: Unexpected number
for...of中使用Generator
函数
for...of
会自动遍历Generator
函数,且不需要再调用 next
方法.
function *foo(){
yield 1;
yield 2;
yield 3;
yield 4;
return 5;
}
for(let i of foo()){
console.log(i)
} //1 2 3 4
上面的代码使用
for...of
循环4条yield
语句的值,一旦next
方法的返回对象的done
属性为true,for...of
循环就会终止,且不包含改返回对象,所以上面的return语句返回的6不包括在for...of
循环中。
for...of
语句,扩展运算符(···
)、解构赋值和Array.from
方法内部调用的都是遍历器接口。这意味着它们可以将Generator
函数返回的Iterator
对象作为参数。
function *numbers(){
yield 1;
yield 2;
return 3;
yield 4;
}
//扩展运算符
[...numbers()]//[ 1, 2 ];
//Array.from
Array.from(numbers()) //[ 1,2 ]
//解构赋值
let [ x, y ] = numbers()
x //1
y //2
//for...of
for (let i of numbers()){
console.log(i)
}
//1
//2
Generator.prototype.throw();
Generator
函数返回的遍历器都有一个throw
方法,可以在函数体外抛出错误,然后在体内捕获。
var g = function *(){
while(true){
try{
yield;
}catch(e){
if(e!='a') throw e
console.log('内部捕获',e)
}
}
}
var i = g();
i.next();
try{
i.throw('a');
i.throw('b');
}catch(e){
console.log('外部捕获',e);
}
//内部捕获 a
//外部捕获 b
上面代码中,遍历器对象i
连续抛出错误。第一个错误被Generator
函数体内catch
捕获,然后Generator
函数执行完成,于是第二个错误被函数体外的catch
语句捕获。
Generator.prototype.return();
-
Generator
函数返回的遍历器对象还有一个return方法,可以返回给定的值,并终结Generator
函数的遍历。
function *numbers(){
yield 1;
yield 2;
return 3;
yield 4;
}
var num = numbers();
num.next();
// {value: 1, done: false}
num.return('stop');
//{value: "stop", done: true}
num.next();
//{value: undefined, done: true}
上面函数在遍历器
num
调用return
方法后,遍历终止,返回值的done
为true
,之后调用next方法总是会返回true
- 如果
Generator
函数内部有try...finally代码块,namereturn
方法会推迟到finally代码块执行完再执行。
function *numbers(){
try{
yield 1;
yield 2;
}finally{
yield 3;
yield 4;
}
yield 5;
};
var num = numbers();
num.next();
//{value: 1, done: false}
num.next();
//{value: 2, done: false}
num.return();
//{value: 3, done: false}
num.next();
//{{value: 4, done: false}
num.next();
//{value: 6, done: true}
总结
Generator
函数是分段执行的,yield
语句是暂停执行的标记,而next
方法可以恢复执行。Generator
函数它返回一个遍历器对象,代表Generator
函数的内部指针。Generator
函数它可以返回一系列的值,因为它可以有人以多条yield
语句。yield
语句是Generator
函数特有的(也仅限于它),将紧跟在yield
后的表达式的值作为返回的对象的value
属性值。- 使用
for...of
循环运行的Generator
函数,不需要调用next
方法,在next
的返回对象done
属性为true时,for...of
循环终止。(扩展运算符,Array.from,解构赋值,都可以使用Generator
函数返回的Iterator
对象作为参数)
网友评论