1.生成器(Generator) #

在JavaScript中,生成器(Generator)是ES6引入的一种新的数据类型,它允许你定义一个函数,该函数可以根据需要一次返回(产出)一个值,而不是立即返回一个最终值。这使得它可以在需要时生成值,这对于处理大量数据或需要“懒加载”的场景特别有用。

1.1 基本语法 #

生成器函数的定义与普通函数非常相似,但它使用了*标识,并且使用yield关键字返回值。

function* simpleGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

1.2. 使用生成器 #

当你调用一个生成器函数时,它不会立即执行,而是返回一个迭代器对象。使用这个对象的next()方法可以从生成器中获取下一个值。

// 创建一个简单的生成器
const gen = simpleGenerator();
// 获取生成器的第一个值并打印
console.log(gen.next());  // { value: 1, done: false }
// 获取生成器的第二个值并打印
console.log(gen.next());  // { value: 2, done: false }
// 获取生成器的第三个值并打印
console.log(gen.next());  // { value: 3, done: false }
// 继续调用,但生成器没有更多的值可以提供
console.log(gen.next());  // { value: undefined, done: true }

每次调用next()都会执行生成器函数,直到遇到下一个yield,然后暂停执行并返回yield后的值。当没有更多的值可以yield时,done属性为true

1.3. 使用yield与数据交互 #

除了从生成器返回值,你还可以发送值回生成器。这是通过在next()调用中传递一个参数来实现的:

// 定义一个可以接收输入的生成器函数
function* generatorWithInput() {
  const input = yield;     // 从外部接收一个值,并赋值给input
  console.log(input);      // 打印接收到的值
}
// 创建生成器实例
const gen = generatorWithInput();
// 启动生成器,此时它等待输入
gen.next();
// 向生成器传入字符串'Hello!',并继续执行
gen.next('Hello!');  // 输出: Hello!

1.4. runSaga #

// 定义一个名为rootSaga的生成器函数
function* rootSaga() {
    yield { type: 'PUT', action: { type: "ADD" } };           // 发送一个PUT类型的动作,动作类型为ADD
    yield new Promise(resolve => setTimeout(resolve, 3000))  // 等待3秒的Promise
    yield { type: 'PUT', action: { type: "MINUS" } };        // 再次发送一个PUT类型的动作,动作类型为MINUS
}
// 定义一个执行saga的函数
function runSaga(saga) {
    const it = saga();  // 创建生成器实例
    function next() {   // 定义一个处理生成器值的递归函数
        const { done, value: effect } = it.next();  // 获取生成器的下一个值
        if (!done) {    // 如果生成器没有完成
            if (effect instanceof Promise) {   // 如果值是一个Promise
                effect.then(next);             // 等待Promise完成后继续
            } else if (effect.type === 'PUT') {  // 如果值是一个PUT类型的动作
                console.log(`向仓库派发一个动作${JSON.stringify(effect.action)}`);  // 打印派发的动作
                next();  // 继续执行
            } else {    // 其他情况
                next();  // 直接继续执行
            }
        }
    }
    next();  // 启动递归函数
}
// 运行rootSaga生成器函数
runSaga(rootSaga);

1.5. throw #

// 定义一个名为gen的生成器函数
function * gen(){
    yield 1;  // 产出值1
    yield 2;  // 产出值2
    yield 3;  // 产出值3
}
// 创建gen生成器的迭代器
let it = gen();
// 打印迭代器的[Symbol.iterator]属性,这将展示生成器对象的迭代器方法
console.log(it[Symbol.iterator]);
// 获取迭代器的第一个值
let r1 = it.next();
console.log(r1);  // 输出:{ value: 1, done: false }
// 结束生成器的执行并返回一个给定的值
//let r2 = it.next();
//let r2 = it.throw();
let r2 = it.return();
console.log(r2);  // 输出:{ value: undefined, done: true }
// 试图获取迭代器的下一个值,但生成器已经结束
let r3 = it.next();
console.log(r3);  // 输出:{ value: undefined, done: true }
// 再次尝试获取迭代器的下一个值
let r4 = it.next();
console.log(r4);  // 输出:{ value: undefined, done: true }

2. once #

// 导入events模块中的EventEmitter类
let EventEmitter = require('events');
// 创建一个EventEmitter实例
let e = new EventEmitter();
// 为“click”事件注册一个只会被调用一次的监听器
e.once('click',(data)=>{
    console.log('clicked',data);  // 打印“clicked”和传递的数据
});
// 触发“click”事件并传入字符串'data'作为数据
e.emit('click','data');  // 输出:clicked data
// 再次尝试触发“click”事件,但因为我们使用了.once注册监听器,所以这次不会有输出
e.emit('click','data');