Swoole通过Timer和协程两种方式实现延迟执行:Timer::after用于事件循环中毫秒级回调,co::sleep用于协程内同步风格挂起;需注意Timer无协程上下文、精度误差及内存泄漏风险。

Swoole 本身不提供类似 JavaScript setTimeout 那样的“延迟执行函数”原语,但可以通过 定时器(Timer) 和 协程调度 两种主流方式实现 PHP 中的延迟执行逻辑。关键在于:不是“让当前代码暂停”,而是“在指定时间后触发回调或协程”。
用 Swoole Timer 实现毫秒级延迟回调
适用于需要在事件循环中延后执行一段逻辑(如延迟清理、重试、通知),且不阻塞主线程的场景。
-
使用
Swoole\Timer::after():一次性定时器,到期后执行回调并自动销毁 - 回调中可调用任意 PHP 代码,包括异步 I/O(如
co::sleep()、Co\Http\Client) - 注意:回调运行在事件循环线程中,不能直接访问同步阻塞资源(如
file_get_contents)
示例:
// 延迟 2.5 秒后发送一条日志
Swoole\Timer::after(2500, function () {
echo "[INFO] Delayed task executed at " . date('H:i:s') . "\n";
// 可在此发起协程 HTTP 请求等
go(function () {
$client = new Swoole\Coroutine\Http\Client('httpbin.org', 80);
$client->get('/delay/1');
echo "HTTP done: " . $client->body . "\n";
});
});
用协程 + co::sleep 实现同步风格延迟
适用于协程上下文(如 go() 或 run() 启动的协程中),写法更直观,类似同步 sleep,但不阻塞整个进程。
立即学习“PHP免费学习笔记(深入)”;
-
co::sleep(2.5)表示挂起当前协程 2.5 秒,期间其他协程可继续运行 - 必须在协程内调用,否则会报错 “Cannot use co::sleep outside coroutine”
- 适合流程化延迟逻辑,比如“等待 3 秒再查数据库”
示例:
go(function () {
echo "Start at " . date('H:i:s') . "\n";
co::sleep(3); // 挂起当前协程 3 秒
echo "Resumed at " . date('H:i:s') . "\n";
// 继续后续操作...
});
封装一个延迟执行的协程函数(推荐)
为提升复用性,可封装一个返回 Promise-风格协程的工具函数,便于链式调用或 await。
- 返回一个协程,内部用
co::sleep实现延迟,再执行目标任务 - 支持传入闭包或可调用对象,延迟后执行并返回结果
- 避免重复写
go(function () { co::sleep(); ... })
简易封装示例:
function delay(float $seconds, callable $callback): \Swoole\Coroutine {
return go(function () use ($seconds, $callback) {
co::sleep($seconds);
return $callback();
});
}
// 使用
$job = delay(1.5, function () {
return 'Done after 1.5s';
});
$result = $job->wait(); // 若需等待结果(仅限当前协程内)
echo $result; // 输出:Done after 1.5s
注意事项与避坑点
-
Timer 回调无协程上下文:默认不在协程中运行,若需协程能力(如
co::mysql),须显式go()包裹 - 延迟精度非绝对精确:受事件循环调度影响,通常误差在几毫秒内,不适合实时性要求极高的场景
-
Timer 不跨进程:每个 Worker 进程需单独设置;若用
SWOOLE_PROCESS模式,主进程设的 timer 不影响子进程 -
内存泄漏风险:未 unset 的长周期 timer(如
tick)或忘记clear(),可能累积导致内存增长



















