登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  php教程

PHP Fibers 中异常如何传回调度器

来源:17golang原创

时间:2026-09-12 20:02:02 246浏览 收藏

PHP Fiber 出错时,调度器通常不应该等待一个“错误返回值”。正确的路径是:任务先在 Fiber::suspend() 暂停,调度器发现失败后调用 $fiber->throw($exception),异常会从 Fiber 内那次 suspend() 位置重新抛出;Fiber 内部没有捕获时,异常才继续冒到调度器的 throw() 调用处。

要点速览
  • resume($value) 把值交回暂停点,throw($e) 把异常交回暂停点。
  • Fiber 内的 try/catch 能消费异常;未处理异常会让 Fiber 终止并由调度器捕获。
  • getReturn() 只适合读取正常返回值,异常终止的 Fiber 不能这样读取。

一、先把三次调用的返回点分清

start() 第一次运行 Fiber,直到它暂停或结束。若闭包执行到 Fiber::suspend('waiting')start() 得到的是 waiting。之后调用 resume('ok'),字符串 ok 会成为 Fiber 内那次 suspend() 的返回值。

异常路径完全不同:throw($e) 并不返回一个异常对象,而是在暂停点执行一次抛出。暂停点处于 try 内时,Fiber 可以捕获并继续;没有捕获时,异常从 throw() 调用处继续向上冒泡。

PHP Fiber 的 start suspend resume throw 与调度器之间的静态调用关系框图
图1:操作示意图,查看调度器、Fiber、Fiber::suspend() 暂停点和 Throwable 之间的静态关系。

二、让调度器把异常投递到暂停点

下面的小例子把任务分成“成功”和“失败”两类。代码没有运行外部事件循环,但它完整展示了调度器需要保存的状态:Fiber 是否暂停、成功值交给谁、失败异常从哪里接住。

getMessage();
    }
}

function dispatch(bool $shouldFail): string
{
    $fiber = new Fiber(static fn(): string => runTask($shouldFail));
    $fiber->start(); // 运行到 Fiber::suspend(),得到 waiting。

    try {
        if ($shouldFail) {
            // 异常从暂停点进入,而不是伪装成 resume() 的返回值。
            $fiber->throw(new RuntimeException('remote failed'));
        } else {
            $fiber->resume('resume');
        }
    } catch (Throwable $error) {
        // 未被 Fiber 内部捕获的异常在这里统一处理。
        return 'scheduler-caught: ' . $error->getMessage();
    }

    return $fiber->getReturn(); // 只有正常终止时读取返回值。
}

// 失败任务返回 task-caught: remote failed;成功任务返回 task-ok。
echo dispatch(true), PHP_EOL;
echo dispatch(false), PHP_EOL;
?>

这个例子里 runTask() 捕获了失败,所以 throw() 返回后 Fiber 还能正常终止,getReturn() 读到的是字符串。若删掉 catch,同一个异常会从 $fiber->throw() 冒到调度器的 catch,此时不要再调用 getReturn() 读取结果。

PHP Fiber 调度器投递成功值或 Throwable 并接收正常返回与未处理异常的静态结构框图
图2:结果示意图,查看成功值、Throwable、Fiber 终止状态和调度器捕获边界的静态关系。

三、接入真实异步回调时检查这几个边界

现象原因处理方式
FiberErrorFiber 未启动、正在运行或已经终止调用前检查 isSuspended()
异常进了错误的层回调只保存了错误,没有绑定原 Fiber在回调闭包中保留 Fiber 引用,失败时调用 throw()
getReturn() 报错Fiber 尚未正常结束,或以异常终止只在确认正常终止后读取返回值

在 Promise 或事件循环中,成功回调用 resume($value),失败回调用 throw($reason)。两条回调必须指向同一个暂停中的 Fiber;否则调度器即使拿到了正确异常,也无法把它交回产生等待的代码。Fiber 只是单线程内的协作式切换,不会自动替你完成调度、超时或取消策略。

相关问题

调用 Fiber::throw() 后异常一定会在调度器捕获吗?

不一定。Fiber 内部的 try/catch 可以先消费它;只有未处理异常才会从 throw() 调用处冒出。

为什么不能用 resume(new Exception()) 表示失败?

因为这只是把 Exception 对象当普通值返回给 suspend()。要保持异常语义,应使用 throw()

Fiber 终止后还能再次 resume 吗?

不能。resume()throw() 都要求 Fiber 处于暂停状态,终止后应从调度器队列移除。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>