JavaScript利用生成器函数实现优雅处理异步任务流

 更新时间:2023年07月05日 09:41:30   作者:Leviash  
Generators 是 JavaScript 中的一种特殊函数,它们可以暂停执行并根据需要生成多个值,本文将详细介绍 generators 的作用、用法以及与其他语言特性的配合使用,希望对大家有所帮助

Generators 是 JavaScript 中的一种特殊函数,它们可以暂停执行并根据需要生成多个值。通过 yield 关键字,生成器函数可以在每次被调用时产生一个新值,这使得它们非常适合处理大量数据或需要延迟计算的场景。本文将详细介绍 generators 的作用、用法以及与其他语言特性的配合使用。

1. 生成器函数的定义和使用

生成器函数是通过一个特殊的函数关键字 function* 来定义的。在生成器函数内部,可以使用 yield 关键字来指定需要生成的值。以下是生成器函数的示例:

function* myGenerator() {
    yield 'Apple';
    yield 'Banana';
    yield 'Cherry';
}
const generator = myGenerator();
console.log(generator.next()); // 输出: { value: 'Apple', done: false }
console.log(generator.next()); // 输出: { value: 'Banana ', done: false }
console.log(generator.next()); // 输出: { value: 'Cherry', done: false }
console.log(generator.next()); // 输出: { value: undefined, done: true }

通过调用生成器函数,我们可以获得一个生成器对象 generator。每次调用 generator.next() 都会返回一个包含 value 和 done 属性的对象。

  • value 表示下一个生成的值。
  • done 表示是否还有更多的值需要生成。当所有值都被生成后,done 为 true。

2. 暂停和恢复执行

生成器函数的强大之处在于它们能够暂停和恢复执行,这意味着可以在需要时延迟计算或逐步处理大量数据,而不是一次性全部处理。以下示例展示了如何利用生成器函数处理大型数据集:

function* generateNumbers() {
    for (let i = 0; i <= 1000000; i++) {
        yield i;
    }
}
const numbersGenerator = generateNumbers();
for (let number of numbersGenerator) {
    console.log(number);
}

在上述示例中,我们定义了一个生成器函数 generateNumbers(),它会生成一个从 0 到 1000000 的数字序列。通过 yield 关键字,每次循环都会产生一个新的数字,并在迭代过程中输出到控制台。通过这种方式,我们可以逐步处理巨大的数据集,避免一次性加载整个数据集导致的性能问题。

3. 与其他语言特性的配合使用

生成器函数在与其他 JavaScript 特性结合使用时,可以发挥更大的作用。

Iterator Protocol 迭代器协议 

由于生成器函数返回的是一个可迭代对象,因此可以通过 for...of 循环来逐个访问生成的值。

function* shoppingList() {
    yield 'Milk';
    yield 'Eggs';
    yield 'Bread';
}
const myCart = shoppingList();
for (let item of myCart) {
    console.log(item);
}

解构赋值 

可以通过在生成器函数中使用解构赋值来获取生成的值的特定部分:

function* personDetails() {
    yield ["John", "Doe"];
    yield ["Jane", "Smith"];
}
const fullNameGenerator = personDetails();
for (let [firstName, lastName] of fullNameGenerator) {
    console.log(firstName, lastName);
}

生成器和 Promise 的组合使用 

生成器函数与异步编程结合使用,可以实现更灵活的控制流,简化异步操作的处理。下面我们分别介绍在生成器函数中如何使用 Promise 和 async/await 来处理异步编程。

使用 Promise:

function fetchTodos() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(['Todo 1', 'Todo 2', 'Todo 3']);
    }, 2000);
  });
}
function* todoGenerator() {
  yield fetchTodos();
}
let generator = todoGenerator();
let promise = generator.next().value;
promise.then(todos => {
  console.log(todos);  // ['Todo 1', 'Todo 2', 'Todo 3']
});

在上述代码中,我们定义了一个异步函数 fetchTodos(),它返回一个 Promise 对象,在 2 秒钟后会 resolve 一个包含待办事项的数组。然后,我们定义了一个生成器函数 todoGenerator(),其中通过 yield 关键字将 fetchTodos() 函数作为生成器的值进行暂停。

在生成器对象上调用 next() 方法后,我们可以获取到 fetchTodos() 返回的 Promise 对象,然后可以使用 .then() 方法处理该 Promise 的结果。

使用 async/await:

function fetchTodo() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('Todo');
    }, 2000);
  });
}
function* todoGenerator() {
  try {
    let result = yield fetchTodo();
    console.log(result); // 'Todo'
    console.log('Generator continues...');
    // 可以在生成器函数中根据需要使用多个异步操作
    let anotherResult = yield someAsyncOperation();
    console.log(anotherResult);
    // ...
  } catch (error) {
    console.error(error);
  }
}
async function main() {
  const generator = todoGenerator();
  try {
    while (true) {
      const { value, done } = generator.next();
      if (done) {
        break;
      }
      await value;
    }
  } catch (error) {
    console.error(error);
  }
}
main();

在上面的示例中:

1.fetchTodo() 函数返回一个 Promise 对象,表示获取待办事项。生成器函数 todoGenerator() 使用 yield 暂停执行,并等待该 Promise 结果。

2.在 main() 函数中,我们创建了一个迭代器对象 generator,通过循环并使用 await 关键字来依次执行生成器函数中的异步操作。

3.生成器函数中可以根据需要使用多个异步操作,使用 yield 暂停执行并等待每个操作完成。捕获可能的错误,可以使用 try-catch 块。

PS. 生成器函数本身并不返回 Promise 对象,因此我们需要将生成器函数与 main() 函数结合使用,以确保异步操作按照预期顺序执行。

总的来说,通过在生成器函数中结合 Promise、async/await 等异步编程特性,可以使生成器函数的控制流更加灵活、简洁和可读,从而提升异步编程的开发体验。

委托给另外一个Generator函数

委托(delegating)给另一个 Generator 函数是 Generator 函数在使用上的一种常见用法,它允许一个生成器函数调用另一个生成器函数,并将后者的生成器值逐个 yield 出来。这种委托机制可以简化代码结构,提高可读性,同时灵活地处理多个生成器之间的协作关系。

示例代码:

function* generator1() {
  yield 1;
  yield 2;
}
function* generator2() {
  yield 'a';
  yield 'b';
}
function* combinedGenerator() {
  yield* generator1();  // 委托generator1()
  yield* generator2();  // 委托generator2()
  yield 'Final value';
}
let generator = combinedGenerator();
console.log(generator.next());  // { value: 1, done: false }
console.log(generator.next());  // { value: 2, done: false }
console.log(generator.next());  // { value: 'a', done: false }
console.log(generator.next());  // { value: 'b', done: false }
console.log(generator.next());  // { value: 'Final value', done: false }
console.log(generator.next());  // { value: undefined, done: true }

在上述代码中,我们定义了三个生成器函数:generator1()、generator2() 和 combinedGenerator()。其中,combinedGenerator() 是我们将要创建的委托生成器函数。

在 combinedGenerator() 中,通过使用 yield* 表达式,我们可以将执行权委托给其他生成器函数,即将 generator1() 和 generator2() 的生成器值依次逐个 yield 出来。这样,在使用 combinedGenerator() 生成的生成器对象上调用 next() 方法时,它会检查当前生成器函数是否有委托的生成器函数可供调用。

值得注意的是,通过委托给其他生成器函数,不仅可以在合并生成器值时保持代码的模块化和可复用性,还可以处理更复杂的生成器协作场景。在实际开发中,你还可以根据具体需求嵌套多个委托关系,以实现更灵活和高效的生成器编程。

另外如果在委托生成器函数中发生异常(如:委托的生成器函数中出现错误、被主动生成器函数提前结束),该异常会被传递回主生成器函数并抛出。

通过委托机制,JavaScript 中的 Generator 函数能够更好地组织和控制生成器之间的协作关系,使得代码更具可读性、可维护性,并且支持构建复杂的生成器流程。

到此这篇关于JavaScript利用生成器函数实现优雅处理异步任务流的文章就介绍到这了,更多相关JavaScript生成器函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • javascript倒计时效果实现

    javascript倒计时效果实现

    这篇文章为大家分享了javascript倒计时效果实现代码段,现今团购网、电商网、门户网等,常使用时间记录重要的时刻,如时间显示、倒计时差、限时抢购等,特别是双十一活动,需要的朋友可以参考下
    2015-11-11
  • javascript中setAttribute兼容性用法分析

    javascript中setAttribute兼容性用法分析

    这篇文章主要介绍了javascript中setAttribute兼容性用法,结合实例形式分析了javascript使用setAttribute进行属性设置操作的相关使用技巧,需要的朋友可以参考下
    2016-12-12
  • js实现放大镜特效

    js实现放大镜特效

    这篇文章主要为大家详细介绍了js实现放大镜特效,简单实用的代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • js重写alert事件(避免alert弹框标题出现网址)

    js重写alert事件(避免alert弹框标题出现网址)

    这篇文章主要给大家介绍了关于js重写alert事件的相关资料,这样可以避免alert弹框标题出现网址的情况,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • 再谈querySelector和querySelectorAll的区别与联系

    再谈querySelector和querySelectorAll的区别与联系

    先按W3C的规范来说这两个方法应该返回的内容吧,大家先看下官方的解释,然后根据需要选择使用
    2012-04-04
  • JS Promise axios 请求结果后面的.then() 是什么意思

    JS Promise axios 请求结果后面的.then() 是什么意思

    本文主要介绍了JS Promise axios 请求结果后面的 .then() 是什么意思,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • webpack多页面开发实践

    webpack多页面开发实践

    这篇文章主要介绍了webpack多页面开发实践,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-12-12
  • 利用js实现Vue2.0中数据的双向绑定功能

    利用js实现Vue2.0中数据的双向绑定功能

    vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的,下面这篇文章主要给大家介绍了关于如何利用js实现Vue2.0中数据的双向绑定功能的相关资料,需要的朋友可以参考下
    2021-07-07
  • 新手快速上手webpack4打包工具的使用详解

    新手快速上手webpack4打包工具的使用详解

    这篇文章主要介绍了新手快速上手webpack4打包工具的使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-01-01
  • 基于JavaScript如何制作遮罩层对话框

    基于JavaScript如何制作遮罩层对话框

    遮罩层听起来貌似很复杂,其实说白了就是一个全界面的半透明的div,用户不可以点击下边的元素,或者说是点击没有反应,接下来通过本文给大家介绍JavaScript如何制作遮罩层对话框,对js遮罩层相关知识感兴趣的朋友一起学习吧
    2016-01-01

最新评论