likes
comments
collection
share

js核心之Generator函数

作者站长头像
站长
· 阅读数 3

先有问题再有答案

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

函数运行的三种状态

js核心之Generator函数

进程&线程&协程

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

协程&线程的区别:

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

在 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)

babeljs编译后的代码

参考

es6_generator