IT干货网

Generator函数的语法

developer 2022年03月15日 编程设计 206 0

简介

Generator函数是ES6关于异步编程的解决方案。Generator函数能够让函数暂停执行(即交出函数的执行权),简单直白点来理解,Generator函数就是一个状态机,内部封装了多个状态(暂停执行的位置)

  • 定义:
  1. function 关键字后有一个*
  2. 函数体内部有yield表达式,表示暂停的位置,定义不同状态。
function* helloWorld(){ 
    yield 'hello'; 
    yield 'world'; 
    return 'end'; 
} 
var hw = helloWorld(); 
  • 调用
  1. 通过next()方法调用,返回结果形如{value:xxx, done: bool}。并在下一个yield处暂停。
  2. 当函数执行完毕(碰到return或者所有yield执行完毕),那么返回的结果value为return的值(无return 则返回undefined),done为true。
  3. 函数执行完毕后无论怎么调用next()方法,结果都是{ value: undefined, done: true }
  4. 通过next()可以传入参数进入状态机,即传入值取代yield表达式。
hw.next(); 
//{value:'hello',done:false} 
hw.next(); 
//{value:'world',done:false} 
hw.next(); 
//{value:'end',done:true} 
hw.next() 
//{value:'undefined',done:true} 
  • yield*表达式
    yield*表达式用于在Generator函数内部调用另外一个Generator函数。

  • throw()方法
    在函数体外部抛出错误,如果内部有catch语句,那么优先内部捕获,否则为外部捕获。全局的throw命令只能够外部catch捕获

  • return()方法
    用于终结遍历Generator函数。Generator函数调用return方法后,那么返回值的done变为true,遍历终止。
    当内部有try...finally代码块时,return会推迟到finally代码块执行完后再执行。

function* numbers () { 
  yield 1; 
  try { 
    yield 2; 
    yield 3; 
  } finally { 
    yield 4; 
    yield 5; 
  } 
  yield 6; 
} 
var g = numbers(); 
g.next() // { value: 1, done: false } 
g.next() // { value: 2, done: false } 
g.return(7) // { value: 4, done: false } 
g.next() // { value: 5, done: false } 
g.next() // { value: 7, done: true } 
  • 其他
  1. yield只能在Generator函数内使用
  2. Generator函数执行后会返回一个遍历器对象,通过把Generator函数赋值给对象的Symbol.iterator属性,即可使对象获得Iterator接口,可以被for...of...等遍历

应用

1. 利用Generator遍历Object的属性

function* objectEntries(obj) { 
  let propKeys = Reflect.ownKeys(obj); 
   
  for (let propKey of propKeys) { 
    yield [propKey, obj[propKey]]; 
  } 
} 
 
let jane = { first: 'Jane', last: 'Doe' }; 
 
for (let [key, value] of objectEntries(jane)) { 
  console.log(`${key}: ${value}`); 
} 

2. 利用yield*遍历嵌套数组

let arr = [1,2,[3,4,5,[6,7,8],9],10]; 
 
function* iterArr(arr){ 
	for(let value of arr){ 
		if(Array.isArray(value)){ 
			yield* iterArr(value); 
		}else{ 
			yield value; 
		} 
	} 
} 
for(let x of iterArr(arr)){ 
	console.log(x); 
} 

3. 利用yield*中序遍历完全二叉树

//节点的构造函数 
 
function Node(left, value, right){ 
	this.left = left; 
	this.value = value; 
	this.right = right; 
} 
//完全二叉树遍历函数 
function* inorder(node){ 
	if(node){ 
		yield* inorder(node.left); 
		yield node.value; 
		yield* inorder(node.right); 
	} 
} 
 
//二叉树生成函数 
function binaryTree(arr){ 
	if(arr.length === 1){ 
		return new Node(null, arr[0], null); 
	} 
	return new Node(binaryTree(arr[0]),arr[1],binaryTree(arr[2])); 
} 
 
let tree = binaryTree([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]); 
 
console.log(tree); 
 
let result = []; 
for (let node of inorder(tree)){ 
	result.push(node); 
} 
console.log(result); 

4. Generator函数的状态机实现

Generator 的确是用来实现状态机的最佳数据结构。

首先,在没有外部输入的情况,一个状态机只有内部既定的逻辑,与Generator函数一样:若未经处理,那么也是一个按照既定顺序执行的多阶段的任务队列。

通过yield 可以表示状态机的状态,利用yield来接受外界对Generator的输入,即状态机接受外部的输入参数。
然后通过内部的逻辑,将之切换到不同的下一个yield,即可实现状态机的切换。

提供一个利用Generator函数实现的简单状态机案例:

  • 状态机的状态变化如下图所示:

  • Generator函数模拟
let machine = function* (){ 
	let states = ['初始化','状态一', '状态二', '状态三', '状态四', '退出']; 
	let i = 0; 
	let input = yield states[0]; 
	while(true){ 
		if(typeof input !== 'number'){ 
			input = 0; 
		} 
		switch (i) { 
			case 1: 
				if(input <= 0) i = 3; 
				else if(input > 10) i = 2; 
				else if(input > 100) i = 4; 
				break; 
 
			case 2: 
				if(input > 10) i = 4; 
				break; 
			case 3: 
				if(input > 10) i = 4; 
				else if(input < 0) i = 1; 
				break; 
			case 4: 
				if(input > 100) i = 1; 
				if(input < -100){ 
					yield states[5]; 
					return '状态机关闭'; 
				} 
				break; 
			default: 
				i = 1; 
				break; 
		} 
		input = yield states[i]; 
	} 
}; 
machine = machine(); 
console.log(machine.next()); 
  • 函数执行结果:

参考文献

  1. ECMAScript 6入门--第16章 Generator函数的语法

评论关闭
IT干货网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!