代码截断运行逻辑

思路:状态标记 + Promise 阻塞 + 事件触发" 的逻辑闭环

1.开启

单步模式的开启设置 isStepMode = true

2.初始化时开启:创建实例时传入参数

构造函数会接收 isStepMode 参数,存储为实例属性,并通过 eventBus 通知外部组件(如节点 UI)开启单步模式:

kotlin 复制代码
constructor(data, isStepMode = false) {
  this.isStepMode = isStepMode; // 1. 存储单步模式状态到实例
  this._waitResolve = null; // 2. 初始化 Promise resolve 句柄(用于控制暂停/继续)
  eventBus.emit('update:runType', isStepMode); // 3. 通知外部组件(如BaseCustomNode)切换单步模式UI
  this._initNodes(data);
  this._initEvent(); // 4. 初始化事件监听(关键:监听"继续"事件)
}

3. 暂停逻辑核心

_createWaitPromise 方法(第 127 行)是单步暂停的核心,它通过返回一个 未 resolve 的 Promise 阻塞代码执行,直到外部调用 resolve 才继续:

javascript 复制代码
// 创建暂停Promise(单步模式下阻断递归)
_createWaitPromise(){
  return new Promise(resolve => {
    // 关键:将 Promise 的 resolve 方法保存到实例的 _waitResolve 属性
    // 此时 Promise 处于 pending 状态,代码执行会卡在这个Promise处
    this._waitResolve = resolve
  })
}
  • 原理 :Promise 未调用 resolve 时,后续 await 该 Promise 的代码会一直阻塞(类似 "断点暂停")。
  • 触发时机:在递归执行节点时,判断单步模式开启则调用该方法,实现 "每个节点执行前暂停"。

4.循环暂停 / 继续:递归中的节点执行闭环

递归逻辑,每个节点执行前都会检查单步模式,满足条件则暂停,直到用户触发 "继续"。

节点执行前暂停:判断单步模式并调用 _createWaitPromise

执行每个节点前会先检查是否为 "非开始 / 结束节点" 且单步模式开启,满足则触发暂停:

kotlin 复制代码
  // 非开始/结束节点 + 单步模式开启:触发暂停
  if (!isSkipTruncate) {
    // 1. 通知外部组件
    eventBus.emit('logic:step:status', { nodeId, status: 'ready', nodeLabel });
    if (this.isStepMode) {
      // 2. 调用 _createWaitPromise,返回未resolve的Promise,阻塞后续代码执行
      await this._createWaitPromise(); 
    }
  }
csharp 复制代码
  // 暂停结束后,才执行节点(发送"执行中"状态 + 调用 node.Start())
  eventBus.emit('logic:step:status', { nodeId, status: 'running', nodeLabel });
  let nextNode = await node.Start(); // 执行当前节点
csharp 复制代码
 // 递归执行下一个节点,重复"暂停-执行"循环
  await this._recursiveRun(nextNode, node.nodeIn)

执行完当前节点后,递归调用方法处理下一个节点,再次触发 "暂停检查",实现 "每个节点执行前暂停" 的循环。

5."继续" 触发:外部组件通过 eventBus 调用 resolve

暂停后的 "继续" 是通过外部组件的 "运行" 按钮发送事件, 监听事件后调用 _waitResolve(即 _createWaitPromise 中保存的 resolve 方法),让阻塞的 Promise 完成。

监听 "继续" 事件:_initEvent 方法

_initEvent 方法中,监听 logic:step:continue 事件,收到事件后调用 this._waitResolve() 解除暂停:

javascript 复制代码
_initEvent(){
  // 监听单步继续事件运行按钮触发
  eventBus.on('logic:step:continue', () => {
    if (this._waitResolve) {
      this._waitResolve(); // 1. 调用保存的 resolve 方法,解除 Promise 阻塞
      this._waitResolve = null; // 2. 重置 _waitResolve,避免重复调用
    }
  })
}

外部组件触发 "继续":BaseCustomNode 的 "运行" 按钮

在 外部组件 中,当用户点击 "运行" 按钮时,发送 logic:step:continue 事件,触发继续逻辑

运行按钮

ini 复制代码
const handleRunNode = () => {
  if (stepState.value.status !== 'ready') return;
  // 发送"继续"事件,通知 LogicManager 解除暂停
  eventBus.emit('logic:step:continue');
  // 更新节点状态为"执行中"
  stepState.value.status = 'running';
}
  1. 第一个节点暂停 : 执行第一个非开始 / 结束节点前,调用 _createWaitPromise 阻塞,同时通知 UI 显示 "运行" 按钮。
  2. 用户触发继续 :用户点击 "运行" 按钮,发送 logic:step:continue 事件, 调用 _waitResolve 解除阻塞,执行当前节点。
  3. 递归下一个节点 :当前节点执行完后,_recursiveRun 递归处理下一个节点,重复 "暂停 - 继续" 循环,直到流程结束。
javascript 复制代码
// 1. 执行节点A,触发暂停
async _recursiveRun(节点A, ...) {
  if (this.isStepMode) {
    // 调用 _createWaitPromise,_waitResolve 被赋值为 节点A的resolve
    await this._createWaitPromise(); 
    // 此时 _waitResolve = 节点A的resolve(非null,处于暂停)
  }
}

// 2. 用户点击"继续",触发事件
eventBus.on('logic:step:continue', () => {
  if (this._waitResolve) { 
    this._waitResolve(); // 调用节点A的resolve,释放暂停
    this._waitResolve = null; // 重置为null,节点A的释放完成
  }
});

注意:

只在 _initEvent 中注册一次,但能实现多次触发响应 ,核心原因是 事件监听的 "注册一次,永久生效" 特性 :一旦注册,事件总线会持续监听该事件,每次外部触发 logic:step:continue 时,都会执行回调函数,与注册次数无关。

eventBus 是一个基于 "订阅 - 发布" 模式的事件系统,eventBus.on('logic:step:continue', 回调) 表示 "订阅 logic:step:continue 事件"。

  • 订阅操作(on)只需执行一次,即可长期生效:事件总线会将回调函数存入内部的事件列表中。
  • 后续每次通过 eventBus.emit('logic:step:continue') 发布事件时,事件总线都会从列表中找到对应的回调函数并执行。
kotlin 复制代码
() => {
  if (this._waitResolve) {
    this._waitResolve() // 释放当前暂停
    this._waitResolve = null // 重置
  }
}

每次触发时,它会检查当前实例的 _waitResolve 状态:

  • 若处于暂停状态(_waitResolve 有值),则执行释放逻辑;
  • 释放后立即重置 _waitResolvenull,不影响下一次暂停。这种 "基于实例当前状态动态执行" 的特性,让单次注册的回调能适配多次暂停 - 继续的循环。

只要实例存在,eventBus 中注册的监听就不会失效。在单步调试流程中:

  • 第一个节点暂停时,触发 emit,回调执行释放;
  • 第二个节点暂停时,再次触发 emit,回调再次执行释放;
  • 直到流程结束,实例被销毁前,监听始终有效。
相关推荐
一枚前端小能手4 小时前
「周更第8期」实用JS库推荐:decimal.j
前端·javascript
草莓熊Lotso4 小时前
《C++ Web 自动化测试实战:常用函数全解析与场景化应用指南》
前端·c++·python·dubbo
Tech_Lin4 小时前
JavaScript Date时间对象的常用操作方法总结
前端·javascript
温宇飞4 小时前
JavaScript 异常处理
前端
小岛前端4 小时前
🔥Vue3 移动端组件精选!满足各种场景!
前端·vue.js·微信小程序
用户1510581047434 小时前
带leading和trailing的防抖和节流
前端
IT小哥哥呀4 小时前
论文见解:REACT:在语言模型中协同推理和行动
前端·人工智能·react.js·语言模型
一枚前端小能手4 小时前
🚫 请求取消还在用flag?AbortController让你的异步操作更优雅
前端·javascript
code_YuJun4 小时前
前端脚手架开发流程
前端