场景说明
我们开发了一套守护大脚本,需要实现:APP 写入指令文件 → 大脚本检测指令 → 自动拉起 / 终止另一套业务子脚本,核心依赖 scripts.run() / scripts.stop() 接口调度子脚本。
已测试全部方案及对应故障
尝试方案 1:使用 engines.myEngine().on() 事件监听执行脚本调度
问题:运行直接报错,提示引擎实例不存在 .on() 方法,脚本强制退出。
尝试方案 2:新建独立 setInterval 定时器,定时器内部调用 scripts.run()
问题:定时器上下文内调用 scripts 接口,抛出 ReferenceError: "scripts" 未定义,脚本直接结束。
尝试方案 3:通过 threads.start() 新建独立子线程,子线程循环检测标记并调用 scripts
问题:所有 threads 创建的子线程环境均无法识别 scripts 对象,同样报 scripts 未定义 闪退。
尝试方案 4:脚本最末尾放置主线程同步 while(true) 死循环轮询标记,主线程原生上下文调用 scripts
问题:主线程无限死循环会独占调度资源,阻塞全部后台 threads 子线程(指令文件轮询、心跳、自动弹窗点击等后台循环全部停止,无法执行业务)。
折中留存方案:将脚本启停逻辑嵌入顶层全局心跳 setInterval(仅此上下文可正常调用 scripts 无报错)
现存缺陷:心跳固定 60 秒执行一次,APP 下发启停指令后最长需要等待 1 分钟才能执行调度,实时性完全不满足业务需求。
核心底层疑问
Hamibot 是否存在上下文隔离限制:仅脚本顶层同步代码、顶层初始化创建的 setInterval 能访问 scripts API,子线程 / 新建定时器均无法访问?
有无官方支持的、可高频实时轮询全局标记、同时稳定调用 scripts.run()/scripts.stop() 的实现方式?
如果必须依托顶层定时器,是否支持自定义高频短间隔定时器(1 秒轮询),不会出现上下文丢失、scripts 未定义报错?
主线程无限 while 死循环抢占调度阻塞全部后台 threads 子线程,是否属于底层调度机制限制,有无规避写法?

HAMI_6893877807 · 3 小时前

回答

Hamibot AI
1
· 3 小时前

你的问题核心源于 API 模块名称误用JavaScript 事件循环机制 的冲突。下面逐一解答你的底层疑问,并提供符合 Hamibot 运行环境的稳定调度方案。

一、 核心问题定位

Hamibot 中 不存在 scripts 模块。你遇到的 ReferenceError: "scripts" 未定义 并非上下文隔离限制,而是调用了未声明的全局对象。Hamibot 官方用于脚本调度、引擎管理的模块是 engines。将代码中的 scripts.run() / scripts.stop() 替换为 engines.execScriptFile()execution.getEngine().forceStop() 后,方案 2 和方案 3 的报错会直接消失。

二、 针对底层疑问的解答

1. 是否存在上下文隔离限制?
不存在。engineseventsthreads 等核心模块在 Hamibot 的顶层作用域、setInterval 回调、threads 子线程中均可直接访问。你遇到的“仅顶层定时器能访问”是 scripts 拼写错误导致的假象。

2. 有无官方支持的高频实时轮询 + 稳定调度方式?
有。推荐使用 主线程 setInterval 短间隔轮询events 模块事件驱动。Hamibot 基于 Rhino 引擎,主线程的 setInterval 回调由事件循环统一调度,不会丢失上下文,且能稳定调用 engines API。

3. 是否支持自定义高频短间隔定时器(1 秒轮询)?
完全支持。setInterval(function() {}, 1000) 在 Hamibot 中运行稳定,不会出现上下文丢失或 API 未定义。只要避免在回调内执行耗时同步操作(如大文件读写、长循环),1 秒轮询对性能影响极小。

4. 主线程 while(true) 阻塞后台线程是否属底层限制?有无规避写法?
属于 JavaScript 单线程事件循环的标准机制。主线程同步死循环会持续占用执行栈,导致事件队列中的定时器回调、线程消息、UI 事件永远无法出队执行。规避方法:绝对不要在主线程使用同步 while(true),改用 setIntervalevents.waitFor() 异步模型。


三、 官方推荐的最佳实践代码

以下代码完全兼容 Hamibot 的 Rhino 1.7.15 引擎,采用 1 秒高频轮询指令文件,并妥善管理子脚本生命周期。

// 文档地址 https://docs.hamibot.com/

// 记录子脚本执行实例,用于精准停止
var subExecutionMap = {};

// 模拟读取指令文件,需替换为你的实际文件读取逻辑
function readCommandFile() {
    // 示例:返回 'start' | 'stop' | 'none'
    // 实际可使用 files.read() 或 open() 读取 APP 写入的指令文件
    return 'none'; 
}

function pollAndSchedule() {
    var cmd = readCommandFile();
    
    if (cmd === 'start') {
        if (!subExecutionMap['business']) {
            // 拉起业务子脚本
            subExecutionMap['business'] = engines.execScriptFile('./business.js');
            log('守护脚本:已拉起业务子脚本');
        }
    } else if (cmd === 'stop') {
        if (subExecutionMap['business']) {
            // 终止业务子脚本
            subExecutionMap['business'].getEngine().forceStop();
            delete subExecutionMap['business'];
            log('守护脚本:已终止业务子脚本');
        }
    }
}

// 1秒高频轮询,由事件循环调度,不阻塞其他线程
setInterval(function() {
    pollAndSchedule();
}, 1000);

// 保持守护脚本常驻运行
setInterval(function() {}, 60000);

四、 关键注意事项

  1. 引擎兼容性:Hamibot 使用 Rhino 1.7.15,不支持箭头函数、默认参数、Promise 等 ES6+ 语法。所有回调必须使用 function() {} 声明。
  2. 停止脚本的正确姿势engines.execScriptFile() 返回的是 ScriptExecution 对象。停止脚本必须调用 execution.getEngine().forceStop(),直接操作引擎实例会导致状态不一致。
  3. 文件读写性能:高频轮询指令文件时,确保文件读取逻辑轻量。若文件较大或路径在外部存储,建议将读取操作放入 threads.start() 子线程,通过 threads 的消息机制或全局变量将指令传递给主线程定时器执行 engines 调度,避免 I/O 阻塞事件循环。
  4. 更优的通信方案:如果 APP 与 Hamibot 脚本运行在同一设备,强烈建议放弃文件轮询,改用 events 模块配合广播或 intent。APP 发送广播,Hamibot 使用 events.onBroadcast()events.on() 监听,可实现毫秒级响应且零轮询开销。

按照上述方案替换 scriptsengines,并移除主线程同步死循环,你的守护脚本即可实现高频、实时、稳定的子脚本调度。如有具体文件读取或跨进程通信的实现细节需要优化,可提供代码片段,我会进一步给出针对性调整建议。

ℹ️ 内容由 AI 生成,可能不准确,请注意核实