Generator 是 ES6 为异步编程和迭代器设计的核心特性,本文聚焦 Generator 函数的核心语法:yield 暂停执行、next () 传参机制、yield* 调用迭代器 / 生成器的逻辑,通过极简示例讲清原理;
ES6中也提出了新的循环方式for...of;它是只能用于可生成迭代对象的循环的;比如数组,Set,Map,String,自定义的迭代器等等;提这个知识点是它跟本文的知识点有关联;
一、Generator 定义
Generator主要用于异步编程,最大的特点就是交出函数的执行权(暂停);本质上,整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器;
返回方式为yield;yield命令是异步不同阶段的分界线;
yield和return的区别:
yield:暂停函数,再次调用会执行到遇到下一个yield为止;return:结束函数,再次调用会重新执行,直到return结束;
Generator 函数体内使用yield语句,可以定义不同的内部状态;
函数的调用方法:next()方法;会返回一个对象,{ value: 值,done: 布尔值 }
function* print(){
yield 'a';
yield 'b';
yield 'c';
return 'd ...end';
}
let p = print();
console.log(p.next())
// { value: 'a', done: false}
console.log(p.next())
// { value: 'b', done: false}
console.log(p.next())
// { value: 'c', done: false}
console.log(p.next())
// { value: 'd ...end', done: true}
console.log(p.next())
// { value: undefined, done: true}
当输出完成后done会变成true;接着调用的还是能返回值,但是返回的都是undefined;
二、next()
Generator函数中的.next()方法可以接收参数;
- 传入的参数,其实是把上一个
yield语句的返回的值给覆盖; - 第一个
.next()方法其实就是启动器,在它之前没有yield语句,所以给第一个.next()方法传参是没有意义的
function* generatorFunction() {
const a = yield;
while(true) {
yield a;
}
}
const generator = generatorFunction();
console.log(generator.next()); // { value: undefined, done: false}
console.log(generator.next(1)); // { value: 1, done: false }
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 1, done: false }
第一次调用的时候yield没有返回语句,所以是undefined;第二次调用的时候传1,接收到的是1,往后只会一直输出1;
三、yield 和 yield*
比如创建一个生成器用于返回一个斐波那契数列,斐波那契数列定义如下:
- 第一个数是 0
- 第二个数是 1
- 之后的每一个数是前两个数之和
换言之,即:F(0) = 0; F(1) = 1; ... F(n) = F(n-1) + F(n-2);
实际上我们希望在一个生成器里输出一些来自其它生成器的值。这时 yield* 就派上用场了:
function* fibonacciGeneratorFunction(a = 0, b = 1) {
yield a;
yield* fibonacciGeneratorFunction(b, b + a);
}
const fibonacciGenerator = fibonacciGeneratorFunction();
fibonacciGenerator.next(); // { value: 0, done: false }
fibonacciGenerator.next(); // { value: 1, done: false }
fibonacciGenerator.next(); // { value: 1, done: false }
fibonacciGenerator.next(); // { value: 2, done: false }
fibonacciGenerator.next(); // { value: 3, done: false }
fibonacciGenerator.next(); // { value: 5, done: false }
yield* 是可用于调用其他的生成器的;
function* g1() {
yield 2;
yield 3;
yield 4;
}
function* g2() {
yield 1;
yield* g1();
yield 5;
}
const iterator = g2();
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: 4, done: false}
console.log(iterator.next()); // {value: 5, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
yield* 的用处
yield* 紧跟的表达式可以是任何可生成迭代对象的迭代器或另一个生成器;可生成迭代对象的Object有 Array, String, Set, Map等;可以通过原型链上的Symbol.Iterator判断是否可生成迭代对象;
Symbol.Iterator在Symbol的那一篇(第一篇)有提
四、作用
数组的合并分级遍历
let a = [1,2,3,4]
let b = [5,6,7,8]
// let c = [...a,...b]
// for(let key of c) {
// console.log(key);
// }
function* obPrint(...args) {
console.log(...args);
for(const key of args) {
yield* key
}
}
for(const num of obPrint(a,b)) {
console.log(num);
}
感谢您抽出宝贵的时间观看本文;本文是JavaScript系列的第4篇,后续会持续更新,欢迎关注~