Skip to main content
Bun 内置了对 cron 的支持 — 解析表达式、在进程内按计划运行回调,或注册可在重启后保持的 OS 级别任务。

快速入门

在当前进程中按计划运行回调:
解析 cron 表达式以查找下一个匹配时间:
注册一个按计划运行脚本的 OS 级别 cron 任务:

Bun.cron.parse()

解析 cron 表达式并返回 UTC 时区的下一个匹配 Date

参数

返回值

Date | null — 下一个匹配时间,如果 8 年内没有匹配则返回 null(例如,2 月 30 日)。

链式调用

重复调用 parse() 以获取一系列即将到来的时间:

Cron 表达式语法

标准 5 字段格式:minute hour day-of-month month day-of-week

特殊字符

命名值

月份和星期字段接受不区分大小写的名称:
星期字段中,07 都表示星期日。

预定义昵称

时区

Bun.cron.parse() 和进程内 Bun.cron(schedule, handler)UTC 解释调度。无需处理夏令时 — 0 9 * * * 始终表示 9:00 UTC。 OS 级别的 Bun.cron(path, schedule, title) 使用系统的本地时区,因为 crontab、launchd 和 Windows Task Scheduler 就是这样工作的。要使两种形式一致,请使用 TZ=UTC 运行进程。

日期和星期的交互

同时指定了日期(月)和星期(两者都不是 *)时,表达式在任一条件为真时匹配。这遵循 POSIX cron 标准。
当只指定其中一个(另一个为 *)时,仅使用该字段进行匹配。

Bun.cron(schedule, handler) — 进程内

在当前进程内按 cron 计划运行回调。
进程内调度是长期运行的服务器和 worker 的轻量级选择 — 无需系统 cron 守护进程,在所有平台上工作方式相同,并在调用之间共享状态(数据库连接池、缓存、模块级变量)。

参数

同步返回一个 CronJob。如果表达式无效或没有未来出现机会,则抛出 TypeError,如 "0 0 30 2 *"(2 月 30 日)。

无重叠保证

下一次触发时间仅在处理器(包括其返回的任何 Promise)安顿后计算。如果您的处理器需要 90 秒,而调度是 * * * * *,那么第二次触发是在处理器完成后第一个分钟边界,而不是首次触发后 60 秒。调用永远不会堆叠。

错误处理

错误遵循 setTimeout 语义:
  • 同步 throw 会触发 process.on("uncaughtException")
  • 被 reject 的返回 Promise 会触发 process.on("unhandledRejection")
如果没有监听器,进程以代码 1 退出。有监听器时,任务继续运行 — 它不会在第一次失败时停止。

bun --hot

bun --hot 下,所有进程内 cron 任务会在模块图重新评估之前立即停止。源代码中仍然存在的每个 Bun.cron() 调用会重新注册。编辑调度、编辑处理器或完全删除该行都会在保存时生效,而不会泄漏定时器。

CronJob 句柄

CronJobDisposableusing job = Bun.cron(...) 在作用域退出时自动停止。stop()ref()unref() 都返回该任务以支持链式调用。

假定时器

进程内 cron 遵循 jest.useFakeTimers()setSystemTime()advanceTimersByTime()runAllTimers() 控制其触发时间,因此您可以测试计划的回调而无需等待真实时钟。

Bun.cron(path, schedule, title) — OS 级别

注册一个按计划运行 JavaScript/TypeScript 模块的 OS 级别 cron 任务。

参数

使用相同的 title 重新注册会原地覆盖现有任务 — 旧调度被替换,而不是重复。

scheduled() 处理器

注册的脚本必须导出一个默认对象,其中包含 scheduled() 方法,遵循 Cloudflare Workers Cron Triggers API
worker.ts
处理器可以是 async。Bun 会等待返回的 promise 安顿后才退出。

各平台的工作原理

Linux

Bun 使用 crontab 注册任务。每个任务以一行形式存储在用户的 crontab 中,上方带有 # bun-cron: <title> 标记注释。 crontab 条目如下:
当 cron 守护进程触发任务时,Bun 会导入您的模块并调用 scheduled() 处理器。 查看已注册的任务:
日志: 在 Linux 上,cron 输出发送到系统日志。使用以下命令查看:
要将 stdout/stderr 捕获到文件,直接在 crontab 条目中重定向输出,或在 scheduled() 处理器中添加日志记录。 无需代码手动卸载:

macOS

Bun 使用 launchd 注册任务。每个任务作为 plist 文件安装到:
plist 使用 StartCalendarInterval 定义调度。支持带有范围、列表或步长的复杂模式 — Bun 会将其展开为多个 StartCalendarInterval 字典(笛卡尔积)。 查看已注册的任务:
日志: stdout 和 stderr 写入到:
例如,一个标题为 weekly-report 的任务:
无需代码手动卸载:

Windows

Bun 使用 Windows Task Scheduler 和基于 XML 的任务定义。每个任务注册为名为 bun-cron-<title> 的计划任务,使用 CalendarTrigger 元素和 Repetition 模式。 大多数 cron 表达式得到完全支持,包括 @daily@weekly@monthly@yearly、范围(1-5)、列表(1,15)、命名日期/月份和日期模式。

用户上下文

Bun 使用 S4U(Service-for-User) 登录类型注册任务,即使用户未登录也会以注册用户身份运行任务 — 与 Linux crontab 行为一致。不存储密码。 TCP/IP 网络(fetch()、HTTP、WebSocket、数据库连接)正常工作。唯一的限制是 S4U 任务无法访问 Windows 认证的网络资源(SMB 文件共享、映射驱动器、Kerberos/NTLM 服务)。 在无头服务器和 CI 环境中,如果当前用户的安全标识符(SID)无法解析 — 例如由 NSSM 或类似工具创建的服务帐户 — Bun.cron() 会失败并显示说明问题的错误。解决方法是:以普通用户帐户运行 Bun,或使用 schtasks /create /xml <file> /tn <name> /ru SYSTEM /f 手动创建计划任务。

触发器限制

Windows Task Scheduler 强制执行每个任务 48 个触发器的限制(CalendarTrigger 元素具有 maxOccurs="48")。一些在 Linux 和 macOS 上有效的 cron 表达式在 Windows 上会超过此限制。当模式超过限制时,Bun.cron() 会以错误消息 reject。
在所有平台上都有效的表达式: 在 Windows 上失败的表达式(但在 Linux 和 macOS 上有效): 关键因素是表达式是否可以使用 Repetition 间隔(单个触发器)还是必须展开为单独的 CalendarTrigger 元素。能整除 60 的分钟步长(*/1*/2*/3*/4*/5*/6*/10*/12*/15*/20*/30)使用 Repetition,无论其他字段如何都能工作。不能整除 60 的步长(*/7*/8*/9*/11*/13 等)必须展开,而在 24 小时活跃的情况下,计数很快就会超过 48。 要解决此问题,请简化表达式或限制小时范围:

Windows 容器

Bun.cron() 在 Windows Docker 容器中不受支持。Task Scheduler 服务在 servercorenanoserver 镜像中未运行。对于容器化工作负载,请使用进程内调度器。
查看已注册的任务:
无需代码手动卸载:
或打开任务计划程序(taskschd.msc),找到名为 bun-cron-<title> 的任务,右键单击并删除。

Bun.cron.remove()

按标题移除先前注册的 cron 任务。适用于所有平台。
这会逆转 Bun.cron() 所做的操作: 移除不存在的任务会无错误地 resolve。