news 2026/7/6 21:36:06

js 协程使用示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
js 协程使用示例
/** * CoRunner - 独立协程运行时 * * 不依赖 Trigger 框架,提供 Fork / Wait / Call / WaitFor 语义, * 让异步逻辑写成同步风格。 * * ===== 快速上手 ===== * * function* TestB() { * cc.log("TestB, 111"); * yield CoRunner.Wait(1); // 等待 1 秒 * cc.log("TestB, 222"); * } * * function* TestA() { * cc.log("TestA, 111"); * yield CoRunner.Wait(1); * cc.log("TestA, 222"); * yield CoRunner.Call(TestB); // 调用子协程并等待完成 * yield CoRunner.Wait(1); * cc.log("TestA, 333"); * } * * CoRunner.Fork(TestA); // 启动协程(fire-and-forget) * * ===== 带参数的协程 ===== * * function* MyCo(a, b) { * yield CoRunner.Wait(a); * cc.log(b); * } * CoRunner.Fork(MyCo, 2, "hello"); * yield CoRunner.Call(MyCo, 2, "hello"); * * ===== 取消协程 ===== * * var runner = CoRunner.Fork(TestA); * runner.stop(); // 随时取消 * * ===== 完成回调 ===== * * CoRunner.Fork(TestA).onComplete(function() { * cc.log("TestA 执行完毕"); * }); * * ===== 等待条件 ===== * * yield CoRunner.WaitFor(function() { * return someFlag === true; * }, 0.5); // 每 0.5 秒检查一次 */ var CoRunner = (function () { // ---- Runner 内部类 ---- /** * 协程运行器,管理一个生成器及其嵌套调用栈的生命周期 * @class * @private */ function Runner() { /** @type {Generator|null} 当前正在执行的生成器 */ this._gen = null; /** @type {Array<{gen: Generator}>} 嵌套调用的父协程栈 */ this._stack = []; /** @type {boolean} 是否处于活跃状态 */ this._active = false; /** @type {Function|null} 完成回调 */ this._onComplete = null; } /** * 启动协程 * @param {GeneratorFunction} genFunc * @param {Array} [args] * @returns {Runner} */ Runner.prototype.start = function (genFunc, args) { if (this._active) { GameClient.ErrorMsg("CoRunner: runner is already active"); return this; } this._gen = genFunc.apply(null, args || []); this._active = true; this._stack = []; this._step(); return this; }; /** * 停止协程并清理所有资源 */ Runner.prototype.stop = function () { if (!this._active) return; this._active = false; this._stack = []; var gen = this._gen; this._gen = null; if (gen) { // 异步关闭生成器,避免在 next() 过程中关闭导致异常 TriggerCom.TriggerUtils.CloseCo(gen); } this._onComplete = null; }; /** * 设置完成回调(链式调用) * @param {Function} cb * @returns {Runner} */ Runner.prototype.onComplete = function (cb) { this._onComplete = cb; return this; }; // -- 内部方法 -- /** * 推进协程一步 * @private */ Runner.prototype._step = function () { if (!this._active || !this._gen) return; var result; var gen = this._gen; try { result = gen.next(); } catch (e) { this._active = false; this._gen = null; GameClient.ErrorMsg("CoRunner error: " + (e && e.message ? e.message : e)); return; } if (result.done) { this._onGenDone(); return; } this._handleInstruction(result.value); }; /** * 解析 yield 出来的指令对象 * @private */ Runner.prototype._handleInstruction = function (inst) { if (!inst || !inst.__co_inst) { // 不是 CoRunner 指令(如 yield null)→ 立即继续 this._step(); return; } switch (inst.type) { case 'wait': this._doWait(inst.duration); break; case 'call': this._doCall(inst.func, inst.args); break; case 'waitFor': this._doWaitFor(inst.check, inst.interval); break; default: this._step(); break; } }; /** * 等待 N 秒后恢复 * @private */ Runner.prototype._doWait = function (duration) { var self = this; if (duration <= 0) { // 等 0 秒 → 下一帧继续 GameClient.g_timerManager.m_timer.AddTimer(0, -1, function () { if (self._active) self._step(); }); return; } GameClient.g_timerManager.m_timer.AddTimer(duration, -1, function () { if (self._active) self._step(); }); }; /** * 调用子协程并等待完成 * @private */ Runner.prototype._doCall = function (genFunc, args) { // 保存当前生成器到调用栈 this._stack.push({ gen: this._gen }); // 切换到子协程 this._gen = genFunc.apply(null, args || []); this._step(); }; /** * 等待条件成立(轮询模式) * @private */ Runner.prototype._doWaitFor = function (checkFn, interval) { var self = this; interval = interval || 0.1; function poll() { if (!self._active) return; var ok = false; try { ok = !!checkFn(); } catch (e) { // 检查函数异常 → 终止等待继续执行 ok = true; } if (ok) { self._step(); } else { GameClient.g_timerManager.m_timer.AddTimer(interval, -1, poll); } } poll(); }; /** * 当前生成器执行完毕后的处理 * @private */ Runner.prototype._onGenDone = function () { if (this._stack.length > 0) { // 有父协程 → 恢复父协程 var ctx = this._stack.pop(); this._gen = ctx.gen; this._step(); } else { // 根协程执行完毕 → 触发完成回调 this._active = false; this._gen = null; if (this._onComplete) { var cb = this._onComplete; this._onComplete = null; try { cb(); } catch (e) {} } } }; // ---- 公开 API ---- var Co = { /** * 启动一个协程(fire-and-forget),返回 Runner 可链式调用 .onComplete() 或 .stop() * * @param {GeneratorFunction} genFunc - 生成器函数 * @param {...*} args - 传给 genFunc 的参数 * @returns {Runner} * * @example * CoRunner.Fork(TestA); * var r = CoRunner.Fork(TestA, arg1, arg2); * r.onComplete(function() { cc.log("done"); }); * r.stop(); // 取消 */ Fork: function (genFunc) { var args = Array.prototype.slice.call(arguments, 1); var runner = new Runner(); runner.start(genFunc, args); return runner; }, /** * 等待 N 秒(必须在协程内 yield 使用) * * @param {number} seconds * @returns {{__co_inst: true, type: 'wait', duration: number}} * * @example * yield CoRunner.Wait(2.5); */ Wait: function (seconds) { return { __co_inst: true, type: 'wait', duration: seconds }; }, /** * 调用子协程并等待完成(必须在协程内 yield 使用) * * @param {GeneratorFunction} genFunc * @param {...*} args * @returns {{__co_inst: true, type: 'call', func: GeneratorFunction, args: Array}} * * @example * yield CoRunner.Call(SubCo); * yield CoRunner.Call(SubCo, arg1, arg2); */ Call: function (genFunc) { var args = Array.prototype.slice.call(arguments, 1); return { __co_inst: true, type: 'call', func: genFunc, args: args }; }, /** * 等待某个条件成立(轮询检查,必须在协程内 yield 使用) * * @param {Function} checkFn - 返回 boolean 的检查函数 * @param {number} [interval=0.1] - 轮询间隔(秒) * @returns {{__co_inst: true, type: 'waitFor', check: Function, interval: number}} * * @example * yield CoRunner.WaitFor(function() { return someVar >= 100; }, 0.5); */ WaitFor: function (checkFn, interval) { return { __co_inst: true, type: 'waitFor', check: checkFn, interval: interval || 0.1 }; }, /** * 创建一个 Runner 实例(高级用法,手动管理生命周期) * * @returns {Runner} * * @example * var runner = CoRunner.Create(); * runner.start(MyCoroutine, [arg1, arg2]); * runner.onComplete(function() { ... }); */ Create: function () { return new Runner(); } }; return Co; })(); // ╔══════════════════════════════════════════════════════════════════════════════╗ // ║ 完 整 用 法 示 例 ║ // ╚══════════════════════════════════════════════════════════════════════════════╝ // ------------------------------------------------------------------ // 1. 基本协程:启动、等待、顺序执行 // ------------------------------------------------------------------ /* function* BasicCo() { cc.log("开始执行基本协程"); yield CoRunner.Wait(2); // 等待 2 秒 cc.log("2 秒后继续"); yield CoRunner.Wait(1); cc.log("再 1 秒后,协程结束"); } // 启动(fire-and-forget) CoRunner.Fork(BasicCo); */ // ------------------------------------------------------------------ // 2. 带参数的协程 // ------------------------------------------------------------------ /* function* GreetCo(name, delay) { cc.log("Hello, " + name); yield CoRunner.Wait(delay); cc.log("Goodbye, " + name); } CoRunner.Fork(GreetCo, "Alice", 3); CoRunner.Fork(GreetCo, "Bob", 1); // Bob 会先说完 */ // ------------------------------------------------------------------ // 3. 嵌套协程(Call - 等待子协程完成) // ------------------------------------------------------------------ /* function* ChildCo(id) { cc.log("Child " + id + " 开始"); yield CoRunner.Wait(1); cc.log("Child " + id + " 结束"); } function* ParentCo() { cc.log("Parent 开始"); yield CoRunner.Call(ChildCo, 1); // 等待 Child-1 完成 yield CoRunner.Call(ChildCo, 2); // 等待 Child-2 完成 cc.log("Parent 结束"); } CoRunner.Fork(ParentCo); // 输出顺序: // Parent 开始 // Child 1 开始 → (1 秒后) Child 1 结束 // Child 2 开始 → (1 秒后) Child 2 结束 // Parent 结束 */ // ------------------------------------------------------------------ // 4. 深层嵌套(Call 嵌套 Call) // ------------------------------------------------------------------ /* function* DeepC() { cc.log("DeepC"); yield CoRunner.Wait(0.5); cc.log("DeepC done"); } function* DeepB() { cc.log("DeepB"); yield CoRunner.Wait(0.5); yield CoRunner.Call(DeepC); // B 等待 C 完成 cc.log("DeepB done"); } function* DeepA() { cc.log("DeepA"); yield CoRunner.Call(DeepB); // A 等待 B(B 内部又等待 C) cc.log("DeepA done"); } CoRunner.Fork(DeepA); // 输出顺序:DeepA → DeepB → DeepC → DeepC done → DeepB done → DeepA done */ // ------------------------------------------------------------------ // 5. 条件等待(WaitFor - 轮询直到条件成立) // ------------------------------------------------------------------ /* var g_flag = false; // 另一个协程在 3 秒后设标志 CoRunner.Fork(function*() { yield CoRunner.Wait(3); g_flag = true; cc.log("标志已设置"); }); // 等待标志 CoRunner.Fork(function*() { cc.log("开始等待标志..."); yield CoRunner.WaitFor(function() { return g_flag; }, 0.5); // 每 0.5 秒检查一次 cc.log("标志成立,继续执行!"); }); */ // ------------------------------------------------------------------ // 6. 循环中的条件等待(游戏关卡常用模式) // ------------------------------------------------------------------ /* CoRunner.Fork(function*() { cc.log("开始游戏主循环"); // 先等 0.5 秒初始延迟 yield CoRunner.Wait(0.5); while (true) { // 每帧检查战斗是否结束 if (SomeGameAPI.IsBattleOver()) { cc.log("战斗结束,退出循环"); return; } // 执行每轮的业务逻辑 cc.log("刷一波怪"); SomeGameAPI.SpawnEnemies(10); // 等待 7 秒后继续下一轮 yield CoRunner.Wait(7); } }); */ // ------------------------------------------------------------------ // 7. 多个条件并联等待(哪个先满足就继续) // ------------------------------------------------------------------ /* CoRunner.Fork(function*() { var done = false; // Fork 两个子协程并行等待不同条件 CoRunner.Fork(function*() { yield CoRunner.WaitFor(function() { return done; }, 0.05); // 条件 A: 5 秒超时 yield CoRunner.Wait(5); if (!done) { done = true; cc.log("5 秒超时触发"); } }); CoRunner.Fork(function*() { yield CoRunner.WaitFor(function() { return done; }, 0.05); // 条件 B: 敌人死亡 while (!done) { if (SomeGameAPI.IsBossDead()) { done = true; cc.log("Boss 死亡触发"); return; } yield CoRunner.Wait(0.2); } }); // 等任一条件满足 yield CoRunner.WaitFor(function() { return done; }); cc.log("某个条件触发了,继续主流程"); }); */ // ------------------------------------------------------------------ // 8. 树形嵌套调用(复制目录结构等场景) // ------------------------------------------------------------------ /* function* LeafTask(name) { cc.log(" > 处理叶子节点: " + name); yield CoRunner.Wait(0.3); cc.log(" < 叶子节点完成: " + name); } function* BranchTask(name) { cc.log(" > 进入分支: " + name); yield CoRunner.Call(LeafTask, name + ".a"); yield CoRunner.Call(LeafTask, name + ".b"); cc.log(" < 分支完成: " + name); } function* RootTask() { cc.log("开始处理根节点"); yield CoRunner.Call(BranchTask, "章节1"); yield CoRunner.Call(BranchTask, "章节2"); cc.log("所有任务完成"); } CoRunner.Fork(RootTask); */ // ------------------------------------------------------------------ // 9. 取消协程(stop) // ------------------------------------------------------------------ /* var runner = CoRunner.Fork(function*() { cc.log("这个协程会被提前取消"); yield CoRunner.Wait(10); // 永远走不到这里 cc.log("不会执行"); }); // 1.5 秒后取消 CoRunner.Fork(function*() { yield CoRunner.Wait(1.5); runner.stop(); cc.log("协程已被取消"); }); */ // ------------------------------------------------------------------ // 10. 完成回调(onComplete) // ------------------------------------------------------------------ /* CoRunner.Fork(function*() { yield CoRunner.Wait(1); cc.log("协程主体执行中..."); yield CoRunner.Wait(1); }).onComplete(function() { cc.log("协程执行完毕,触发回调"); }); */ // ------------------------------------------------------------------ // 11. 在触发器 Handler 中使用 CoRunner // ------------------------------------------------------------------ /* // 用法 A:在 Action 函数内部 Fork 独立协程(推荐,不改动现有框架) TriggerGroup.AddTriggerExtend(triggerIdList, [new TriggerEvent.LoopTime([0.5, false])], function(acInfo, args) { // ← 普通函数,不是 generator CoRunner.Fork(function*() { if (TriggerAction.IsFightOver()) return; yield CoRunner.Wait(0.1); while (true) { if (TriggerAction.IsFightOver()) return; // ... 刷兵逻辑 ... TriggerGroup.RunOneTrigger(rushList, { rush: 10, count: 1 }); yield CoRunner.Wait(7); } }); } ); // 用法 B:保存 runner 引用,在 OnDestroy 中清理 var g_mainLoopRunner = null; // ... 在 InitTrigger 内部: g_mainLoopRunner = CoRunner.Fork(function*() { // ... 游戏主循环 ... }); // ... 在 OnDestroy 中: // if (g_mainLoopRunner) g_mainLoopRunner.stop(); */ // ------------------------------------------------------------------ // 12. 与项目现有 API 混合使用 // ------------------------------------------------------------------ /* CoRunner.Fork(function*() { cc.log("===== 混合使用示例 ====="); // CoRunner 自己的等待 yield CoRunner.Wait(1); // 直接调用项目的同步 API(不需要 yield) TriggerAction.ShowStory(5); TriggerAction.ShowTips("提示信息", TriggerDef.color.YELLOW); // 等待某个单位死亡 var bossId = TriggerAction.CreateMonster(999, TriggerDef.CAMP.ORC, pos); yield CoRunner.WaitFor(function() { return SomeAPI.IsUnitDead(bossId); }, 0.2); // 设置游戏结果 TriggerAction.SetGameResult(TriggerDef.GAME_RESULT.WIN); cc.log("===== 混合使用完成 ====="); }); */ // ------------------------------------------------------------------ // 13. 手动管理 Runner 生命周期(Create 高级用法) // ------------------------------------------------------------------ /* var myRunner = CoRunner.Create(); // ... 稍后在合适的时机启动 ... myRunner.start(function*() { yield CoRunner.Wait(2); cc.log("手动管理的协程执行完毕"); }, []); myRunner.onComplete(function() { cc.log("手动管理的协程回调"); }); // ... 需要时停止 ... // myRunner.stop(); */ // ------------------------------------------------------------------ // 14. 错误处理 // ------------------------------------------------------------------ /* CoRunner.Fork(function*() { cc.log("正常执行 1"); yield CoRunner.Wait(1); cc.log("正常执行 2"); // 如果协程内抛出异常,Runner 会调用 GameClient.ErrorMsg 记录, // 并终止当前协程(但不会影响其他协程) throw new Error("这里出错了"); cc.log("永远不会执行到这里"); }); // 其他协程不受影响 CoRunner.Fork(function*() { yield CoRunner.Wait(2); cc.log("另一个协程正常完成"); }); */

20260705 用AI更新了一版协程写法,比较好用了; 有不懂的地方也问AI;

仿照Lua的协程写法;

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/6 21:35:35

初入编程世界感觉眼花缭乱?

眼花缭乱有没有&#xff1f;抽象理解第一个C语言程序1. C语言是什么概括&#xff1a;多用于底层开发的&#xff0c;面向过程的编程语言2. 上来就写程序&#xff1f;我开始也觉得不合理&#xff0c;哪有上来就敲代码嘛...QAQ其实不然&#xff0c;一个最简单的“Hello world”程序…

作者头像 李华
网站建设 2026/7/6 21:35:18

Navicat无限试用终极方案:3分钟解决macOS试用期到期难题

Navicat无限试用终极方案&#xff1a;3分钟解决macOS试用期到期难题 【免费下载链接】navicat_reset_mac navicat mac版无限重置试用期脚本 Navicat Mac Version Unlimited Trial Reset Script 项目地址: https://gitcode.com/gh_mirrors/na/navicat_reset_mac 你是否正…

作者头像 李华
网站建设 2026/7/6 21:35:05

Excel数据模型实战:用星型模式构建可扩展分析体系

1. 项目概述&#xff1a;为什么在Excel里建数据模型&#xff0c;不是“多此一举”&#xff0c;而是“不得不做”你有没有遇到过这样的场景&#xff1a;销售报表里客户名称、产品编号、地区代码全混在一个大表里&#xff0c;每次想按省份看Top 10客户&#xff0c;就得手动筛选复…

作者头像 李华
网站建设 2026/7/6 21:34:42

Python自动化登录office_online并获取cookie

Python自动化登录office_online并获取cookie一、安装selenium库和火狐浏览器及驱动1、Windows系统安装火狐浏览器和驱动2、Linux系统安装火狐浏览器和驱动安装 firefox 浏览器安装火狐浏览器驱动二、编写自动化登录的python脚本1、在windows系统2、在Linux系统一、安装selenium…

作者头像 李华
网站建设 2026/7/6 21:34:29

VsCode更换MarkDown样式到底能有多好看?

vs code到底有多强大&#xff1f; 先看看MarkDown笔记的预览效果这只是一种插件 谁再让我用别的软件写代码 我就把这篇文章扔过去 如何操作&#xff1f; 1、安装插件&#xff1a;在扩展中搜索Markdown Preview Enhance2、点击【扩展设置】3、点击【查看】—【命令面板】4、在搜…

作者头像 李华
网站建设 2026/7/6 21:34:16

Go代码混淆实战:garble与现代Go模块的高效集成指南

Go代码混淆实战&#xff1a;garble与现代Go模块的高效集成指南 【免费下载链接】garble Obfuscate Go builds 项目地址: https://gitcode.com/gh_mirrors/ga/garble 在当今商业软件开发的激烈竞争中&#xff0c;保护源代码知识产权已成为开发者必须面对的重要课题。虽然…

作者头像 李华