如何使用 Generator 函数和 yield 关键字实现一个复杂的状态机?

状态机是一种由状态、转移条件和动作组成的计算模型,任意时刻仅处于一个状态,如登录流程可用其建模。Generator函数通过yield暂停执行,适合用同步写法控制状态流转,如实现包含“未登录”“登录中”“已登录”“锁定”的认证状态机,每次调用next()传入事件触发状态切换,并返回当前状态,结合yield特性可清晰表达复杂状态逻辑,支持注入副作用,适用于工作流与UI导航等场景。

如何使用 generator 函数和 yield 关键字实现一个复杂的状态机?

在 JavaScript 中,利用 Generator 函数和 yield 关键字可以非常优雅地实现一个复杂的状态机。Generator 能暂停和恢复执行的特性,天然适合表达状态流转。

什么是状态机?

状态机是一种计算模型,由一组状态、转移条件和动作组成。任意时刻只能处于一个状态,根据输入触发状态之间的切换。比如自动售货机、登录流程、游戏 AI 都可用状态机建模。

Generator 的核心优势

Generator 函数通过 *function 定义,使用 yield 暂停执行,每次调用 next() 才继续。这让我们可以用同步写法表达异步或分步逻辑,非常适合控制状态流转。

特点包括:

函数执行可中断,保留上下文通过 next(value) 向内部传值yield 表达式的返回值是 next() 传入的参数

实现一个带事件驱动的状态机

下面是一个模拟用户认证流程的状态机:包含“未登录”、“登录中”、“已登录”、“锁定”四种状态。

function* authStateMachine() {  let state = 'idle';  let attempts = 0;  const maxAttempts = 3;  while (true) {    const { type, password } = yield state;    if (state === 'idle') {      if (type === 'LOGIN') {        if (password === 'secret') {          state = 'authenticated';        } else {          attempts++;          if (attempts >= maxAttempts) {            state = 'locked';          } else {            state = 'pending';          }        }      }    } else if (state === 'pending') {      if (type === 'LOGIN') {        if (password === 'secret') {          state = 'authenticated';        } else {          attempts++;          if (attempts >= maxAttempts) {            state = 'locked';          }        }      }    } else if (state === 'authenticated') {      if (type === 'LOGOUT') {        state = 'idle';        attempts = 0;      }    } else if (state === 'locked') {      if (type === 'RESET') {        state = 'idle';        attempts = 0;      }    }  }}

如何使用这个状态机

创建实例并驱动状态变化:

const machine = authStateMachine();console.log(machine.next().value); // 输出: idleconsole.log(machine.next({ type: 'LOGIN', password: 'wrong' }).value); // pendingconsole.log(machine.next({ type: 'LOGIN', password: 'wrong' }).value); // pendingconsole.log(machine.next({ type: 'LOGIN', password: 'wrong' }).value); // lockedconsole.log(machine.next({ type: 'RESET' }).value);                   // idleconsole.log(machine.next({ type: 'LOGIN', password: 'secret' }).value); // authenticated

每一步调用 next(input) 相当于发送一个事件,状态机根据当前状态和输入决定下一个状态,并通过 yield state 返回当前状态。

扩展思路:支持异步操作与副作用

可以在状态转移时插入副作用,比如记录日志、发请求等:

if (state === 'idle' && type === 'LOGIN') {  console.log('正在验证...');  // 模拟异步验证  await new Promise(resolve => setTimeout(resolve, 100));  if (password === 'secret') {    state = 'authenticated';  }}

注意:如果加入 async/await,就无法再用普通 Generator 控制。此时可结合 Thunk 或 Promise 包装器,或者改用 async generatorasync function*)配合 for await...of 处理。

总结:Generator + yield 提供了一种线性、清晰的方式来编写复杂状态逻辑。相比一堆 if/else 或状态表,它更易读、易维护,尤其适合工作流、协议解析、UI 导航等场景。

基本上就这些。

以上就是如何使用 Generator 函数和 yield 关键字实现一个复杂的状态机?的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1527684.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月20日 19:37:55
下一篇 2025年12月20日 19:38:09

相关推荐

  • 使用Drupal修改Slick滑块库的配置

    本文旨在帮助Drupal开发者理解如何在不修改Slick滑块库核心文件的前提下,定制Slick滑块的行为。通过JavaScript初始化配置,您可以轻松地调整滑块的各项参数,如自动播放速度、动画效果等,从而满足网站的特定需求。本文将提供详细的示例代码和步骤,确保您可以安全有效地应用这些更改。 Sli…

    好文分享 2025年12月21日
    000
  • PyScript py-repl 会话内容捕获与集成指南

    本文详细介绍了如何在 pyscript 的 `py-repl` 环境中捕获用户输入的 python 代码,这对于教学、作业提交或代码记录等场景至关重要。主要探讨了两种方法:利用 `py-repl` 元素的 `getpysrc()` 方法获取当前代码,以及通过 pyscript 新增的插件方法 `be…

    2025年12月21日
    000
  • 获取当前日期之前的日期:JavaScript 教程

    本文将介绍如何使用 JavaScript 获取当前日期之前的日期。通过 `Date` 对象和 `setDate()` 方法,我们可以轻松地计算出指定日期之前任意天数的日期。本文将提供详细的代码示例和解释,帮助你理解和应用这一技术。 JavaScript 提供了强大的日期处理功能,允许开发者轻松地进行…

    2025年12月21日
    000
  • Node.js 循环中错误处理的策略与实践

    本文探讨在 node.js 中处理循环内部错误的不同策略,旨在控制循环结束后代码的执行流程。我们将介绍如何在发生错误时立即中断循环,以及如何在继续完整循环的同时,通过设置标志位来阻止后续的成功提示,从而根据业务逻辑精确管理错误响应。 在 Node.js 应用开发中,循环是常见的控制结构,用于重复执行…

    2025年12月21日
    000
  • JavaScript中将日期转换为英国时间(GMT/BST)

    本文介绍了如何使用 JavaScript 将日期转换为英国时间,并自动处理夏令时(DST)的转换。通过 Date.toLocaleString() 方法和 Europe/London 时区,可以轻松实现 GMT(冬季)和 BST(夏季)之间的切换,确保时间显示的准确性。 在 JavaScript 中…

    2025年12月21日
    000
  • 动态内容展示:基于活动类名实现元素切换的教程

    本教程旨在详细阐述如何通过检测元素的“active”类名,动态且高效地切换相关联的内容展示。我们将摒弃传统多重if/else判断的低效方式,采用基于类名匹配的通用策略,结合html、css和javascript(jquery),实现平滑的内容过渡效果,适用于轮播图导航等多种场景,提升代码的可维护性和…

    2025年12月21日
    000
  • JavaScript打包工具原理分析

    JavaScript打包工具通过模块解析、代码转换和优化策略,将分散模块整合为浏览器可执行的静态资源。从入口文件开始,利用AST分析依赖关系,构建模块图谱,并支持非JS资源引入;通过Babel等工具进行语法降级,借助loader和插件机制处理各类文件类型;最终封装模块作用域,实现运行时模块系统,支持…

    2025年12月21日
    000
  • JavaScript解构赋值模式匹配算法

    JavaScript的解构赋值基于结构相似性实现数据提取,非真正模式匹配。它通过语法糖按索引或键名直接映射:数组按位置、对象按属性名逐层提取,支持嵌套与默认值,但无回溯、守卫或多分支机制。 JavaScript 的解构赋值并不是一种“模式匹配算法”像在函数式语言(如 Haskell 或 Elixir…

    2025年12月21日
    000
  • JavaScript单例模式与全局状态

    单例模式确保一个类仅有一个实例并提供全局访问点。在JavaScript中可通过对象字面量、闭包惰性初始化或ES6静态属性实现,如Config类示例所示,多次实例化仍返回同一对象。它常用于管理全局状态,如配置、日志等,优点是节省资源,缺点是隐藏依赖、影响测试和导致状态混乱。现代替代方案包括依赖注入、R…

    2025年12月21日
    000
  • 使用Drupal修改Slick滑块库配置的最佳实践

    本文旨在指导Drupal开发者如何在不修改Slick滑块库核心文件的前提下,通过初始化配置灵活地定制Slick滑块的行为。我们将介绍如何通过JavaScript代码修改Slick滑块的各项参数,例如自动播放速度、动画效果等,并提供详细的示例代码和注意事项,帮助您轻松实现个性化的滑块效果。 在Drup…

    2025年12月21日
    000
  • PyScript py-repl 会话内容获取与保存指南

    本文旨在指导开发者如何在 pyscript 的 `py-repl` 环境中获取用户输入的 python 代码,以便进行保存、提交或进一步处理。主要介绍两种方法:利用 `py-repl` 元素的 `getpysrc()` 方法获取当前代码内容,以及通过 pyscript 新版插件系统提供的 `befo…

    2025年12月21日
    000
  • JavaScript异步编程深度解析

    JavaScript异步编程基于事件循环机制,通过宏任务与微任务队列协调执行;从回调函数、Promise、Generator到async/await逐步演进,解决了回调地狱与错误处理问题;合理使用Promise.all、try/catch及并发控制可提升性能与代码可维护性。 JavaScript异步…

    2025年12月21日
    000
  • JavaScript中检查对象数组是否包含特定键值对并返回布尔值

    本文将详细介绍如何在javascript中高效地判断一个对象数组是否包含具有特定键值对的元素,并返回一个布尔值。我们将探讨两种主要方法:传统的循环遍历和现代的array.prototype.some()方法,并对比它们的特点、使用场景及代码实现,帮助开发者选择最适合的方案。 在JavaScript应…

    2025年12月21日
    000
  • 掌握 Vue.js v-if 多条件渲染:从基础语法到计算属性的最佳实践

    本教程探讨了在 vue.js 中使用 `v-if` 进行多条件渲染的正确方法与优化策略。我们将从常见的语法错误入手,纠正 `v-if` 中条件链式判断的写法,并强调避免 `v-if` 与 `v-for` 同时作用于同一元素的反模式。最终,推荐通过使用计算属性(`computed` property)…

    2025年12月21日
    000
  • Vue.js v-if条件渲染:从基础语法到高级优化

    本文深入探讨vue.js中`v-if`指令的多条件链式判断方法。首先纠正常见的语法错误,接着介绍vue风格指南中关于`v-if`与`v-for`同时使用的注意事项,并提供使用“标签进行分离的解决方案。最后,重点推荐并演示如何利用计算属性(`computed`)进行数据预过滤,以实现更高效…

    2025年12月21日
    000
  • 使用 JavaScript 获取当前用户的用户名

    本文旨在介绍如何在 JavaScript 中获取当前用户的用户名。由于 JavaScript 运行在客户端,而用户身份验证通常在服务器端进行,因此我们需要借助服务器端技术(如 PHP)将用户名传递给 JavaScript。本文将提供一种使用 PHP 的 `$_SESSION` 变量将用户名传递给 J…

    2025年12月21日
    000
  • 利用JavaScript与CSS高效实现基于‘active’类的动态内容切换

    本文探讨了如何通过检测元素上的`active`类,实现关联内容的动态显示与隐藏。针对传统多`if-else`判断的局限性,提供了一种更具通用性和可扩展性的解决方案,结合事件监听和动态类名构建,优化了内容切换逻辑,适用于轮播图等交互场景,显著提升代码的可维护性。 引言:动态内容切换的需求 在现代Web…

    2025年12月21日
    000
  • 解决JS跟随光标元素在CSS动画上消失的问题:z-index应用指南

    本文旨在解决javascript控制的跟随光标元素在与css关键帧动画元素交互时出现消失的问题。核心解决方案是利用css的`z-index`属性,通过调整元素的堆叠顺序,确保跟随光标的元素始终保持在动画元素的上方可见。文章将详细阐述`z-index`的工作原理及其在实际应用中的配置方法。 在现代网页…

    2025年12月21日
    000
  • Joi 验证:如何使用 xor 实现字段的互斥必填逻辑

    本文深入探讨了 joi 验证库中 `xor` 方法的使用,旨在解决当两个字段中必须且只能存在一个时,如何实现其互斥必填的验证逻辑。通过具体示例,我们将展示 `xor` 如何确保数据结构满足“非此即彼”的业务规则,从而提高数据质量和应用健壮性。 引言:理解互斥必填场景 在数据模型设计和API验证中,我…

    2025年12月21日
    000
  • Remix Form提交后数据刷新与字段清空策略

    本文深入探讨remix应用中,`form`组件在成功提交后数据未能按预期刷新的问题,尤其是在重定向到同页时。核心原因在于react的组件协调机制,它在重渲染时不会自动更新`defaultvalue`。文章详细阐述了如何利用`key`属性强制组件重新挂载以实现数据刷新,并提供了两种`key`策略(`l…

    2025年12月21日
    000

发表回复

登录后才能评论
关注微信