wezterm.sleep_ms 详解:在 WezTerm Lua 配置脚本中实现毫秒级延迟挂起
【免费下载链接】weztermA GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust项目地址: https://gitcode.com/GitHub_Trending/we/wezterm
wezterm.sleep_ms(milliseconds)是 WezTerm Lua 配置与事件脚本中用于"按指定毫秒数挂起当前脚本执行"的实用工具函数。当你需要在事件回调(如wezterm.on注册的快捷键处理器)中等待异步操作完成、或在编写定时/顺序执行逻辑时,它提供了一条最直接的延迟途径。读完本文,你将掌握该函数的准确语义、源码级实现原理、典型使用场景以及它与wezterm.time.call_after等替代方案的选择要点。
函数签名与基本语义
该函数在 docs/config/lua/wezterm/sleep_ms.md 中有明确说明,自版本20201031-154415-9614e117起可用:
wezterm.sleep_ms(milliseconds)- 参数:
milliseconds,整数(u64),指定挂起的时间长度,单位为毫秒。 - 返回值:无。
- 行为:
wezterm.sleep_ms会挂起(suspend)当前脚本的执行,等待指定的毫秒数过后,脚本从下一条语句继续运行。
这段语义描述非常直接:它是"脚本级别的同步延迟",调用后当前 Lua 协程会暂停指定时间,然后再继续执行后续语句。例如下面这段代码会先打印start,等待 1 秒后再打印end:
print('start') wezterm.sleep_ms(1000) print('end') -- 在调用约 1 秒后执行源码级实现:异步计时器而非忙等
wezterm.sleep_ms并非通过忙等(busy-wait)或阻塞系统调用来浪费 CPU,其实现位于 lua-api-crates/time-funcs/src/lib.rs:
async fn sleep_ms<'lua>(_: &'lua Lua, milliseconds: u64) -> mlua::Result<()> { let duration = std::time::Duration::from_millis(milliseconds); smol::Timer::after(duration).await; Ok(()) }从源码可以看到三个关键事实:
- 参数类型为
u64整数毫秒:函数直接使用 Rust 标准库的std::time::Duration::from_millis(milliseconds)将毫秒数转换为Duration。这决定了你传入的值必须是整数毫秒;如果需要秒级延迟,应自行乘以 1000。 - 底层采用
smol::Timer异步计时器:smol::Timer::after(duration).await是一个基于事件循环的非阻塞计时器。等待期间不会轮询占用 CPU,而是由 WezTerm 的事件循环在超时后唤醒协程。 - 注册为异步函数:在 lua-api-crates/time-funcs/src/lib.rs 中,该函数通过
lua.create_async_function(sleep_ms)注册到wezterm模块:
// For backwards compatibility let wezterm_mod = get_or_create_module(lua, "wezterm")?; wezterm_mod.set("sleep_ms", lua.create_async_function(sleep_ms)?)?;这意味着它在 Lua 侧以协程(coroutine)的形式被挂起,而不是冻结整个宿主进程。这一点对于理解下面"事件回调中的使用"至关重要:在一个事件回调中调用sleep_ms只会挂起当前脚本流程,WezTerm 的 GUI 主循环依然可以继续响应其他事件。
实战示例:等待异步进程就绪后再清理临时文件
wezterm.sleep_ms最典型的实战用途,出现在"把终端滚动缓冲内容发送给外部编辑器"的场景中。该示例同时收录于 docs/config/lua/wezterm/on.md 与 docs/config/lua/pane/get_lines_as_text.md,是官方文档直接给出的完整用法:
local io = require 'io' local os = require 'os' local act = wezterm.action wezterm.on('trigger-vim-with-scrollback', function(window, pane) -- Retrieve the text from the pane local text = pane:get_lines_as_text(pane:get_dimensions().scrollback_rows) -- Create a temporary file to pass to vim local name = os.tmpname() local f = io.open(name, 'w+') f:write(text) f:flush() f:close() -- Open a new window running vim and tell it to open the file window:perform_action( act.SpawnCommandInNewWindow { args = { 'vim', name }, }, pane ) -- Wait "enough" time for vim to read the file before we remove it. -- The window creation and process spawn are asynchronous wrt. running -- this script and are not awaitable, so we just pick a number. -- -- Note: We don't strictly need to remove this file, but it is nice -- to avoid cluttering up the temporary directory. wezterm.sleep_ms(1000) os.remove(name) end) return { keys = { { key = 'E', mods = 'CTRL', action = act.EmitEvent 'trigger-vim-with-scrollback', }, }, }按下CTRL+E后,脚本会把当前 pane 的滚动缓冲全文写入一个临时文件,并启动一个新窗口运行vim打开它。这里的关键难点在于:
- 窗口创建与进程启动是异步的,在该 Lua 脚本中既无法直接等待,也没有可供 await 的句柄;
- 如果立即执行
os.remove(name)删除临时文件,vim可能还没读取到文件内容就发现文件已消失。
官方注释明确解释了这个设计取舍:"The window creation and process spawn are asynchronous wrt. running this script and are not awaitable, so we just pick a number."—— 由于无法对异步过程取 await,只能"选一个足够大的数值"来兜底,因此用wezterm.sleep_ms(1000)挂起 1 秒后再删除临时文件,平衡了"给 vim 足够读取时间"与"不长期占用临时目录"两个诉求。
这个模式可以推广到任何"启动外部程序后,脚本需要等它初始化完成再做后续清理/收尾"的场景,例如:
- 启动编辑器/查看器读取临时文件后延时删除;
- 启动某个守护进程后延时发送初始化命令;
- 需要按固定间隔依次执行多项任务的顺序脚本。
使用注意事项
- 值取整数毫秒,过大需谨慎:参数以毫秒为整数单位,
wezterm.sleep_ms(1000)表示 1 秒。如果传给脚本一个超大数值,脚本会长时间处于挂起状态,期间该协程内的后续逻辑(如清理动作、状态更新)都不会执行。 - 挂起的是当前脚本流程而非整个进程:因为底层是
smol::Timer异步计时器 + Lua 异步函数,等待期间 WezTerm 其余功能不受影响。这与在wezterm.on回调中配合使用是安全的。 - 属于"脚本级同步延迟":在回调内它表现为顺序执行到该行时暂停,语义直观;但它并不适合用来做周期性任务——如果需要"延迟一段时间后执行某段逻辑",更地道的做法是使用
wezterm.time.call_after(见下文对比)。 - 事件回调场景需自行评估等待时长:如官方示例所示,当需要等待外部异步进程(窗口创建、程序启动)时,脚本无法获知对方真实就绪时刻,只能预估一个经验值。延时过短可能导致清理过早,过长则拖慢后续流程,应根据实际程序启动耗时权衡。
与其他时间类 API 的对比与选择
wezterm.sleep_ms位于 WezTerm 的wezterm顶层模块,而在 lua-api-crates/time-funcs/src/lib.rs 中还注册了wezterm.time子模块,提供了另一组时间能力,两者用途互补:
| 函数 | 参数单位 | 语义 | 适用场景 |
|---|---|---|---|
wezterm.sleep_ms(ms) | 整数毫秒 | 挂起当前脚本,随后继续执行下一条语句 | 顺序脚本中的同步延迟、等待异步进程兜底 |
wezterm.time.call_after(interval_seconds, func) | 浮点秒 | 在指定秒数后调度回调函数,不阻塞当前流程 | 定时/延时任务,如周期性状态栏刷新 |
wezterm.time.now() | — | 获取当前 UTC 时间对象 | 时间戳与格式化输出 |
wezterm.time.parse_rfc3339(s)/wezterm.time.parse(s, fmt) | 字符串 | 按 RFC3339 或自定义格式解析时间 | 解析外部时间数据 |
从实现上看,call_after通过ScheduledEvent与配置代际(generation)机制在配置重载后重新调度定时回调,并与配置重载协同以避免回调翻倍执行(见 lua-api-crates/time-funcs/src/lib.rs);而sleep_ms只负责"挂起-唤醒"这一件简单的事。选择原则可以概括为:需要阻塞式顺序等待就用sleep_ms,需要"到点后执行"的非阻塞定时任务就用call_after。
小结
wezterm.sleep_ms(milliseconds)自版本20201031-154415-9614e117起可用,用于按整数毫秒挂起当前 Lua 脚本并随后继续执行;- 其底层实现是 lua-api-crates/time-funcs/src/lib.rs 中的
smol::Timer::after(...).await,为异步非阻塞计时器; - 官方文档给出的典型用例是"启动 vim 后延时删除临时文件"(见 docs/config/lua/wezterm/on.md),适用于一切等待异步进程就绪的兜底延迟场景;
- 对"到点执行"类定时需求,优先考虑非阻塞的
wezterm.time.call_after,避免不必要的脚本挂起。
【免费下载链接】weztermA GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust项目地址: https://gitcode.com/GitHub_Trending/we/wezterm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考