js核心之Generator函数
先有问题再有答案
js中函数有几种运行状态
Generator是什么
Generator有什么特性
Generator有哪些应用场景
babel在低版本是如何处理Generator的
函数运行的三种状态
进程&线程&协程
进程
是计算机分配资源的最小单位
线程
是操作系统执行的最小单位
协程
是程序可以在任何地方暂停,然后在稍后的某个时间点从暂停的地方恢复执行。Generator函数是协程在 ES6 的实现
。
协程&线程的区别:
- 并发性:线程是操作系统级别的并发单位,可以同时运行在多个处理器上,实现
真正的并行执行
。而协程是程序级别
的,它们在同一时间只能有一个在运行,实现的是并发而非并行
。 - 切换开销:线程的切换需要涉及到操作系统的介入,保存和恢复线程上下文,开销相对较大。而协程的切换只需要在程序级别保存和恢复上下文,开销较小。
- 控制方式:线程的调度由操作系统负责,程序员无法直接控制。而协程的调度由程序员在代码中显式控制,可以精确地控制何时挂起和恢复协程。
- 数据安全:由于线程可以并行执行,因此需要使用锁等机制来防止数据竞争。而协程由于在同一时间只有一个在运行,因此不存在数据竞争的问题。
在 JavaScript 中,由于其单线程的特性,协程(通常通过Generator或 async/await 实现)被广泛用于处理异步操作,使得异步代码可以以类似同步的方式编写和理解。
Generator特性
可交出执行权,可暂停可恢复
function* test() {
console.log('1111');
yield;
console.log('3333');
}
const g = test();
g.next();
console.log('2222')
// 打印 1111 2222
再次调用g.next() 从yield处恢复执行
// 打印 3333
可在函数运行时动态注入数据
function* test() {
console.log('1111');
const p = yield;
console.log('2222')
if(p === '3333'){
console.log('p', p);
}
}
const g = test();
g.next();
g.next('3333'); // 这里输入的参数 改变了函数运行结果
在函数执行到一半 动态传入一些参数 改变函数的执行结果 这是js中其他函数不具备的能力
。
可优化性能 按需执行.
假如有一组字符串例如1243-123-234-6-456....形式,需要我们找出是否包含666666的数字,这个输入的字符串又很大,我们直接将字符串转换为数组会很占用内存,这个时候也可以使用generator函数。
function* find(str){
let c = '';
let i = 0;
while(true){
if(!str[i]){
yield c;
return;
}
if(str[i]=== '-'){
yield c;
c = '';
i++;
continue;
}
c += str[i];
i++;
}
}
const gen = find('1243-123-234-6-456')
console.log(gen.next().value) // 1243
console.log(gen.next().value) // 123
console.log(gen.next().value) // 234 ...
Generator应用场景
异步场景
Generator是协程在es6的实现 可以说天然就是为了异步而生的。不过由于Generator&yield使用起来不方便,返回的是一个迭代器需要手动运行解析 因此有了async&await做进一步的封装。
实现迭代器Iterator
let obj = {
name: 'test',
};
[...obj] // Uncaught TypeError: obj is not iterable
let obj = {
name: 'test',
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
};
[...obj] // [1, 2, 3]
对象不支持迭代 这里通过与symbol配合实现了迭代的能力。
状态机
生成器函数可以用来实现状态机。状态机是一种抽象的计算模型,它可以处于一系列的状态中的一个,且在任一时刻,只能处于这些状态中的一个。生成器函数使用 yield 关键字来暂停函数的执行,然后使用生成器的 next 方法来恢复执行,这正好满足了状态机的特性。
function* trafficLight() {
while (true) {
yield { color: 'green', duration: 5000 };
yield { color: 'yellow', duration: 3000 };
yield { color: 'red', duration: 2000 };
}
}
const light = trafficLight();
function changeLight() {
const state = light.next().value;
console.log(state.color);
setTimeout(changeLight, state.duration);
}
changeLight();
使用Generator实现一个交通信号灯的状态机。
babel&Generator源码
facebook的源码 regenerator
一个例子:
function* foo() {
yield 'result1'
yield 'result2'
yield 'result3'
}
const gen = foo()
console.log(gen.next().value)
console.log(gen.next().value)
console.log(gen.next().value)
参考
转载自:https://juejin.cn/post/7337241651620757567