📜  怎样理解 JavaScript 中的 Generator函数?(1)

📅  最后修改于: 2023-12-03 15:09:55.042000             🧑  作者: Mango

怎样理解 JavaScript 中的 Generator 函数?

Generator 函数是 ES6 新增的一种函数类型。与普通函数不同的是,Generator 函数可以控制自己的执行流程,尤其是可以暂停执行并在需要时再从暂停的位置继续执行。这使得 Generator 函数具有了很多有趣的应用场景,比如异步编程、迭代器的生成等等。

Generator 函数的基本语法

Generator 函数的定义方式与普通函数基本相同,但是在函数名前面有一个 * 符号,如下所示:

function* generator() {
  yield 'foo';
  yield 'bar';
  return 'done';
}

在 Generator 函数中,我们可以使用 yield 关键字来暂停函数的执行,并向外部返回一个值。使用 yield 返回的值可以通过函数的 next() 方法获得,如下所示:

const gen = generator();

console.log(gen.next()); // 输出 {value: 'foo', done: false}
console.log(gen.next()); // 输出 {value: 'bar', done: false}
console.log(gen.next()); // 输出 {value: 'done', done: true}

在上面的例子中,我们创建了一个 Generator 函数 generator(),并通过 gen.next() 方法依次调用了它返回的三个值。在第一次调用 next() 方法时,generator() 函数执行到了第一个 yield 关键字处并返回了值 'foo',同时暂停了函数的执行。第二次调用 next() 方法时,generator() 函数从暂停的位置继续执行,并执行了第二个 yield 关键字,返回了值 'bar'。最后一次调用 next() 方法时,generator() 函数继续执行并执行了 return 语句,函数的执行结束并返回了值 'done'

Generator 函数的应用场景
异步编程

异步编程是 JavaScript 中一个非常重要的主题,而 Generator 函数可以使得异步编程变得更加方便、易于理解。比如说,我们可以使用 Generator 函数来实现一个类似于 async/await 的异步操作,如下所示:

function* async() {
  try {
    const data1 = yield fetch('https://api1.example.com');
    const data2 = yield fetch(`https://api2.example.com/${data1}`);
    const data3 = yield fetch(`https://api3.example.com/${data2}`);
    console.log(data3);
  } catch (error) {
    console.error(error);
  }
}

const gen = async();
const result1 = gen.next();
result1.value.then(data1 => {
  const result2 = gen.next(data1);
  result2.value.then(data2 => {
    const result3 = gen.next(data2);
    result3.value.then(data3 => {
      gen.next(data3);
    }).catch(error => {
      gen.throw(error);
    });
  }).catch(error => {
    gen.throw(error);
  });
}).catch(error => {
  gen.throw(error);
});

在上面的例子中,我们定义了一个 Generator 函数 async(),用于异步从三个不同的 API 接口中获取数据。在函数中,我们通过 yield 关键字暂停了函数的执行,由外部代码负责调用 next() 方法并传递数据,然后继续执行函数的下一步操作。在 try 块中,我们使用了三个 yield 关键字来获取三个不同的 API 数据,并最终将三个数据合并在一起打印输出。

由于 fetch() 函数返回的是 Promise,所以我们可以直接调用 then() 方法来获取异步请求的结果。在外部代码中,我们依次调用了 async() 函数返回的三个 Promise,并将每次调用的结果传递给 next() 方法。在每次调用 next() 方法后,我们都使用 then() 方法来获取 Generator 函数的返回结果,并依次传递给下一个 next() 方法。在调用 next() 方法的过程中,我们可能会遇到一些错误,比如网络请求失败等等,此时可以使用 throw() 方法将错误抛回到 Generator 函数中,从而执行 catch 块中的代码。

迭代器的生成

Generator 函数还可以用来生成迭代器对象。在 JavaScript 中,我们可以使用迭代器来遍历一些集合类型的数据,比如数组、Map、Set、字符串等等。我们可以通过定义一个带有 Symbol.iterator 方法的对象来创建一个迭代器,该方法返回的应该是一个包含 next() 方法和一个 done 属性的对象。而在 Generator 函数中,我们可以使用 yield 关键字依次返回迭代器中的每一个值。

下面是一个创建迭代器的例子:

const myIterable = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
};

for (const value of myIterable) {
  console.log(value);
}
// 输出:
// 1
// 2
// 3

在上面的例子中,我们定义了一个对象 myIterable,并在其中定义了一个 Symbol.iterator 方法,该方法返回了一个迭代器。在迭代器中,我们使用 yield 关键字依次返回了值 123。在外部代码中,我们使用 for...of 循环遍历了 myIterable 迭代器中的每一个值,并将其打印输出。

总结

Generator 函数是 JavaScript 中一个非常强大的函数类型,它可以控制自己的执行流程并暂停函数的执行,具有很多有趣的应用场景。在异步编程方面,我们可以使用 Generator 函数来实现类似于 async/await 的操作,并通过暂停函数的执行来处理异步操作。在迭代器方面,我们可以使用 Generator 函数来生成迭代器,并通过 yield 关键字依次返回迭代器中的每一个值。