js的yield与generator

yield是什么

  • yield是ES6的新关键字,可以使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器版本的return关键字。
  • yield关键字实际返回一个IteratorResult(迭代器)对象,它有两个属性,value和done,分别代表返回值和是否完成。点击查看更多关于IteratorResult的知识
  • yield无法单独使用,需要配合generator(生成器)的其他函数,如next,懒汉式操作,展现强大的主动控制特性。

1.yield的写法

function* fn() {
	yield 1;
	yield 2;
	yield 3;
	return 4;
	yield 5;
}

let iterator = fn();

let j = 5;
let timer = setInterval(() => {
	const i = iterator.next();
	if (j-- == 0) {
		clearInterval(timer)
		timer = null;
		return
	}
	console.log(i)
}, 1000)
//结果:
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
// { value: 4, done: true }
// { value: undefined, done: true }
  1. yield必须写在生成器函数generator中;
  2. yield可以用来加强控制,懒汉式加载;
  3. 需要next()函数配合使用,每次调用会执行下一个yield,返回两个值:分别是value和done,代表迭代结果和是否完成;
  4. return之后的yield不会再执行;
  5. return就代表着迭代完成
  6. 没有写retrun的其实函数内部默认返回了return undefined
  7. next()可无限调用,但既定循环完成之后总是返回undeinded

2.next()函数及传参

function* fn(_name) {
	let name = yield _name; //yield默认返回undefined,想要返回其他值,则通过.next(value)来传递
	let age = yield name;
	return {
		name,
		age
	};
}

let i = fn('henry');
console.log(i.next());
console.log(i.next());
console.log(i.next(26)); //只能通过next赋值
//结果:
// { value: 'henry', done: false }
// { value: undefined, done: false }
// { value: { name: undefined, age: 26 }, done: true }

从上面的结果可以看出,每一次.next()括号里面的值都会临时赋值给generator函数中上一个yield,第一次next因为没有上一个yield,所有不会有临时赋值行为。
上面的例子中,第二次next没有传值,所以fn函数中的第一个yield后面的_name会被临时赋值成undefined,所以name等于undefined;第三次next传值26,所以第二个yield后面的name会被临时赋值成26,所以age等于26;但是无论是第二次next的_name或者第三次next的name的实际值都不会真的被赋值,只是临时性的等价于。

从而得出以下结论:

  1. 函数next()是个迭代器对象,传参可以缺省,缺省时相当于undefined,默认调用函数;
  2. yield并不能直接生产值,而是产生一个等待输出的函数;
  3. next()可以带一个参数,该参数会被认为是上一个yield整体的返回值。

next的意义在于,可以在不同阶段从外部直接向内部注入不同的值来调整函数的行为(这一点是其他循环很难做到的,或要付出较大的代价才可以做到)

PS:另外有点要注意的是,如果yield在其他表达式中,需要用()单独括起来,例如:

console.log('my qq:' + yield '123456') //报错 SyntaxError:Unexpected...
console.log('my qq:' + (yield '123456'))

3.抛异常

function * fn(){
	let qq;
	try{
		qq = yield; // yield默认返回undefined,不会抛异常
	}catch(e){
		//内部捕获异常,则不会像外抛出
		console.log('error1')
	}
	console.log(qq)
}

let g = fn();

g.next()
try{
	g.throw('error!')
}catch(e){
	//内部不捕获异常,则会像外抛出
	console.log('error2')
}

4.实际应用

yield可以用generator实现同步方式编写异步流程,比Promise更优雅;

function asyncFun(name) {
	return new Promise(function(resolve) {
		setTimeout(() => {
			resolve('my name is ' + name)
		}, 1000)
	})
}

function sum(a, b) {
	return new Promise(function(resolve) {
		setTimeout(() => {
			console.log('sum')
			resolve(a + b)
		}, 2000)
	})
}

// // 用promise实现
// function fn(name) {
// 	sum(1, 6).then(function(num) {
// 		if (num > 6) {
// 			asyncFun(name).then(function(v) {
// 				console.log(v)
// 			})
// 		} else {
// 			console.log('error!')
// 		}
// 	})
// }
// fn('henry');

//用generator实现
function* fn(name) {
	if ((yield sum(1, 6)) > 6) {
		console.log(yield asyncFun(name))
	} else {
		console.log('error!')
	}
}

const gf = fn('henry');

// 编写一个工具函数,用于执行generator函数
function exec(gf, value) {
	let result = gf.next(value)
	console.log('result', result)
	if (!result.done) {
		if (result.value instanceof Promise) {
			result.value.then(function(v) {
				exec(gf, v)
			})
		} else {
			exec(gf, result.value)
		}
	}
}

exec(gf)

用yield实现异步写法其实很简单,在有异步操作的前面加yield就可以了,比较麻烦的是需要写一个工具函数去执行generator函数,这里我们只是模拟了Promise的异步,可能还有其他,所以我们可以引入比较成熟的第三方库——‘co’;

首先要npm安装以下co;

npm install co;

然后就可以引入了

const co = require('co');

function asyncFun(name) {
	return new Promise(function(resolve) {
		setTimeout(() => {
			resolve('my name is ' + name)
		}, 1000)
	})
}

function sum(a, b) {
	return new Promise(function(resolve) {
		setTimeout(() => {
			console.log('sum')
			resolve(a + b)
		}, 2000)
	})
}

function* fn(name) {
	if ((yield sum(1, 6)) > 6) {
		console.log(yield asyncFun(name))
	} else {
		console.log('error!')
	}
}

var fnx = co.wrap(fn)
fnx('henry')

轻轻松松搞定了,更多关于co库的,点击这里查看项目

最后的最后,我们来做道题目吧!

function* test(x) {
	var y = 2 * (yield(x + 3));
	var z = yield(y / 4);
	console.log('x:' + x + ',y:' + y + ',z:' + z)
	return (x + y + z)
}

var it = test(5);
console.log(it.next())
console.log(it.next())
console.log(it.next())

var gt = test(5)
console.log(gt.next())
console.log(gt.next(16))
console.log(gt.next(21))

打印结果是:

{ value: 8, done: false }
{ value: NaN, done: false }
x:5,y:NaN,z:undefined
{ value: NaN, done: true }
{ value: 8, done: false }
{ value: 8, done: false }
x:5,y:32,z:21
{ value: 58, done: true }

分析请看评论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在JavaScript中,"generator.yield()"这个表达式是不合法的。在Generator函数中,yield关键字被用于暂停和恢复函数的执行。通过调用生成器对象的next()方法,可以使生成器函数从上一个yield语句处恢复执行,并返回一个由yield语句后的表达式值组成的对象。例如,在范例中,通过调用aa.next(),可以使生成器函数从第一个yield语句处恢复执行,并返回一个包含变量a值的对象。同样,在范例中,通过调用g.next(),可以使生成器函数从上一个yield语句处恢复执行,并返回一个包含"happy"的对象。123 #### 引用[.reference_title] - *1* [【ES6】浅谈Generatoryield](https://blog.csdn.net/hhhhhhhssss/article/details/118908976)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] - *2* [Generatoryield用法](https://blog.csdn.net/ZhaoYulin6/article/details/117773330)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] - *3* [生成器对象(Generator)的Generator.prototype.next()方法](https://blog.csdn.net/u013565133/article/details/103028066)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值