js核心之Generator函数

先有问题再有答案

  1. js中函数有几种运行状态
  2. Generator是什么
  3. Generator有什么特性
  4. Generator有哪些应用场景
  5. babel在低版本是如何处理Generator的

函数运行的三种状态

进程&线程&协程

进程是计算机分配资源的最小单位
线程是操作系统执行的最小单位
协程是程序可以在任何地方暂停,然后在稍后的某个时间点从暂停的地方恢复执行。Generator函数是协程在 ES6 的实现

协程&线程的区别:

  1. 并发性 :线程是操作系统级别的并发单位,可以同时运行在多个处理器上,实现真正的并行执行。而协程是程序级别的,它们在同一时间只能有一个在运行,实现的是并发而非并行
  2. 切换开销:线程的切换需要涉及到操作系统的介入,保存和恢复线程上下文,开销相对较大。而协程的切换只需要在程序级别保存和恢复上下文,开销较小。
  3. 控制方式:线程的调度由操作系统负责,程序员无法直接控制。而协程的调度由程序员在代码中显式控制,可以精确地控制何时挂起和恢复协程。
  4. 数据安全:由于线程可以并行执行,因此需要使用锁等机制来防止数据竞争。而协程由于在同一时间只有一个在运行,因此不存在数据竞争的问题。

在 JavaScript 中,由于其单线程的特性,协程(通常通过Generator或 async/await 实现)被广泛用于处理异步操作,使得异步代码可以以类似同步的方式编写和理解。

Generator特性

可交出执行权,可暂停可恢复

javascript 复制代码
function* test() {
    console.log('1111');
    yield;
    console.log('3333');
}

const g = test();
g.next();
console.log('2222')
// 打印 1111  2222

再次调用g.next() 从yield处恢复执行
// 打印 3333

可在函数运行时动态注入数据

javascript 复制代码
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函数。

ini 复制代码
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

ini 复制代码
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 方法来恢复执行,这正好满足了状态机的特性。

scss 复制代码
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

一个例子:

lua 复制代码
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)

babeljs编译后的代码

generator代码解析可以看下这个文章

参考

es6_generator

相关推荐
归于尽2 分钟前
key、JSX、Babel编译、受控组件与非受控组件、虚拟DOM考点解析
前端·react.js·面试
是一碗螺丝粉7 分钟前
📱 移动端CSS十大天坑!我熬夜填平了,快收藏!
前端·css
_请输入用户名17 分钟前
解锁Rollup的原始资源导入能力:rollup-plugin-import-raw完全指南
前端
共享家952718 分钟前
Linux 自旋锁
java·前端·数据库
拉不动的猪22 分钟前
Vue 3 中 async setup () 的「坑」与避坑指南1
前端·javascript·面试
拉不动的猪24 分钟前
Vue 3 中 async setup () 的「坑」与避坑指南2
前端·vue.js·后端
lvchaoq27 分钟前
Vite的优缺点(精简版)
前端
_花卷27 分钟前
🌟ELPIS-如何基于vue3完成领域模型架构
前端·vue.js·架构
讨厌吃蛋黄酥36 分钟前
`useState`是同步还是异步?深入解析闭包陷阱与解决方案
前端·react.js
_一条咸鱼_40 分钟前
Android Runtime调试检测与反制手段(86)
android·面试·android jetpack