希沃ENOW大前端javascript
公司官网:CVTE(广州视源股份)前端
团队:CVTE旗下将来教育希沃软件平台中心enow团队java
本文做者:程序员
遥想当初面试的时候,被问到经典问题ES6都有啥,年纪轻轻的我搜刮了满脑子,除了水就是水。es6
那么ES6都有啥呢,很快脑子里就有let和const、模板字符串、解构赋值、展开运算符、箭头函数等等。说了这些也许足够让面试官展开发挥了,可是优秀的掘金er怎么可能止步于此?面试
为了让面试官眼前一亮,es6还有这些新特性:模块化、Symbol、Set和Map数据结构、Proxy代理和Reflect反射、Generator等等,说完面试官估计眼睛开始有光了,那么说了就要懂,懂了才不怕被面试官问,虽然有点冷门,俗话说技多不压身,今天就一块儿来看看Generator吧!编程
generator即生成器,是ES6规范带来的新内容,在generator可以让咱们在函数执行时任意地方暂停,在后续遇到合适的时机须要使用这个函数时继续执行。以往咱们遇到的函数都是一口气执行到底,而generator的特色就是让函数执行到中间“刹车”,在须要它的时候接着执行。下面从一个案例来看看generator的基本用法吧!markdown
常见的javascript函数:数据结构
function fun() {
console.log(1)
console.log(2)
console.log(3)
}
function run() {
console.log(4)
}
fun()
run()
// 结果:
1
2
3
4
复制代码
使用generator函数:异步
function* funG() {
yield console.log(1)
yield console.log(2)
yield console.log(3)
}
function run() {
console.log(4)
}
const iter = funG()
iter.next()
run()
iter.next()
iter.next()
iter.next()
// 结果:
1
4
2
3
{value: undefined, done:true}
复制代码
写法上:
从打印结果上来看:
知道generator的基本用法了,但还不够,为了加深面试官的印象,咱们能够从generator设计出发点聊聊协程!
既然要聊协程,首先得知道协程是什么吧!
简单来讲协程就像单身程序员小王敲代码,老大给了他一个项目A,小王收到立马开码;
小王项目A作到一半,老大说有个项目B时间赶,赶忙来干项目B;
因而小王中止开发项目A,着手开干项目B;
项目B开发一段时间后,小王回来接着干项目A。
这就是协程,那么项目B作完了?也许没有。
看完了协程的案例,聪明的你应该想到了协程跟generator之间的关系!没错,generator就是协程在js上的实现。经过generator,咱们能够在单线程的JavaScript里使用协程!
generator自己做为异步编程的解决方案,能够用来解决异步任务。除此以外还能够有更灵活的用法!那即是在函数执行过程当中传入参数,以及获取该段表达式输出结果!
上文提到generator每次执行next()方法都会返回一个对象:{ value, done },经过value,咱们能够获取该段表达式的返回结果,另外,next还能够接受参数,利用这一特性,咱们能够随时传入参数!
function* run(name) {
let who = yield name + ' Allen';
return who
}
let flashMan = run('Barry')
flashMan.next() // { value: 'Barry Allen', done: false }
// 传入参数
flashMan.next('Arrow') // { value: 'Allow', done: true }
复制代码
在第一个next()调用时,不传入参数,默认将name的参数值'Barry'与' Allen'组合字符串,这个值被yield返回。
在第二个next()调用时,传入参数'Arrow',这个值被变量who接收,所以返回value属性的值为'Arrow',也就是who的值。
不只如此,generator还能够在函数运行时,捕获函数体外抛出的错误。
function* gen(x){
try {
var y = yield x + 2;
} catch (e){
console.log(e);
}
return y;
}
var g = gen(1);
g.next();
g.throw('error');
// error
复制代码
generator的原理是转化为switch-case来实现的,先从一个简单的案例入手。
function* funG() {
yield 1
yield 2
yield 3
}
const iter = funG()
iter.next() // {value: 1, done: false}
iter.next() // {value: 2, done: false}
iter.next() // {value: 3, done: false}
iter.next() // {value: undefined, done: true}
复制代码
generator的实现须要一个函数,这个函数能够屡次调用,每次返回一个结果,还能够传入参数,那么switch-case就是很好的选择。
function funGen(count) {
switch(count) {
case 1:
return {value: 1, done: false};
case 2:
return {value: 2, done: false};
case 3:
return {value: 3, done: false};
case 'done':
return {value: undefined, done: true}
}
}
funGen(1) // {value: 1, done: false};
funGen(2) // {value: 2, done: false};
funGen(3) // {value: 3, done: false};
funGen('done') // {value: undefined, done: true}
复制代码
从结果上来看是咱们想要的结果,可是距离generator还有很大的差距,接下来建立一个函数,这个函数返回一个对象,经过调用这个对象来帮咱们执行14~17行的语句。
function funGen(count) {
switch(count) {
case 1:
return {value: 1, done: false};
case 2:
return {value: 2, done: false};
case 3:
return {value: 3, done: false};
case 'done':
return {value: undefined, done: true}
}
}
const gen = function() {
let count = 0
return {
next: function() {
++count
count = count > 3 ? 'done' : count
return funGen(count)
}
}
}
const test = gen()
test.next() // {value: 1, done: false}
test.next() // {value: 2, done: false}
test.next() // {value: 3, done: false}
test.next() // {value: undefined, done: true}
复制代码
到目前为止,都须要咱们手动处理函数上下文里的count的值的变化来决定返回结果,因此咱们须要一个对象来保存函数上下文也就是count的值。
function example(context) {
while(1) {
context.pre = context.next
switch(context.pre) {
case 1:
context.next = 2
return 1;
case 2:
context.next = 3
return 2;
case 3:
context.next = 'done'
return 3;
case 'done':
return context.end()
}
}
}
const gen = function() {
return {
next: function() {
value = context.done ? undefined : funGen(context)
done = context.done
return {
value,
done
}
}
}
}
const context = {
pre: 1,
next: 1,
done: false,
end: function end() {
this.done = true
}
}
const test = gen()
test.next() // {value: 1, done: false}
test.next() // {value: 2, done: false}
test.next() // {value: 3, done: false}
test.next() // {value: undefined, done: false}
复制代码
经过一个新对象context来记录函数上下文的初始状态pre,以及下一个状态next,done记录是否到最终点。而end即是修改done为结束状态true。
funGen每次运行next()结束后都会将运行状态保存到context中,方便下次运行时获取。
gernerator做为es6的新特性,在后来被更方便好用的async await代替,可是generator独特的特性可让咱们在函数执行的过程当中传递参数获取结果,使得函数调用变得更加灵活。做为一个开发者,咱们有必要了解一下gernerator的基本使用以及简单的实现的原理,方便在特殊的场景中解决问题。