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

PHP Fiber 挂起后怎么传递异常并让调用方处理

来源:17golang原创

时间:2026-09-08 00:38:26 193浏览 收藏

PHP Fiber 挂起后,异常不是靠共享变量“通知”进去的。只要 Fiber 正停在 Fiber::suspend(),调用方就可以调用 Fiber::throw(),让异常从这个挂起点恢复执行;Fiber 内部能捕获就地处理,不能捕获的才会继续抛回调用方。

要点速览
  • start() 负责第一次运行,resume() 传值,throw() 传异常。
  • throw() 的注入位置是当前 Fiber::suspend() 调用点,不是 Fiber 回调的开头。
  • Fiber 内部已处理的异常要从 getReturn() 取结果;未处理异常则在调用方的 throw() 外捕获。

先把 Fiber 的四个状态动作对上

容易混淆的是“挂起时返回的值”和“Fiber 最终返回的值”。start() 首次执行回调,遇到第一次 suspend() 就把挂起值交给调用方;之后 resume($value) 会把这个值作为挂起表达式的结果。若调用方改用 throw($exception),恢复点得到的不是普通值,而是从 suspend() 位置抛出的异常。

调用发生时机Fiber 内看到什么
start()尚未启动开始执行,直到第一次挂起或直接结束
resume($value)处于挂起态suspend() 表达式得到普通值
throw($e)处于挂起态suspend() 位置抛出 $e
getReturn()已经结束读取回调的最终返回值

让 Fiber 自己处理挂起期间的异常

suspend() 放进 try,就能把调用方注入的异常转换成一个正常的业务结果。下面的例子模拟等待外部资源:调用方发现等待失败后注入 RuntimeException,Fiber 捕获它并返回降级结果。

PHP Fiber::throw 从调用方进入挂起点并由 Fiber 内部 catch 接住的异常边界图
图1:异常从调用方进入当前 Fiber::suspend(),在 Fiber 栈内被 catch 接住后转成最终返回值。
getMessage();
    }
});

echo $fiber->start(), PHP_EOL; // 输出:等待上游响应
$fiber->throw(new RuntimeException('上游超时')); // 结束 Fiber,返回值在 getReturn() 中读取

if ($fiber->isTerminated()) {
    echo $fiber->getReturn(), PHP_EOL; // 输出:已降级:上游超时
}
?>

这里 throw() 本身没有替代 getReturn() 的职责:它只负责恢复 Fiber 并注入异常;回调捕获异常后返回的字符串,仍然要等 Fiber 终止,再通过 getReturn() 读取。

未处理的异常会回到调用方

如果 Fiber 内没有匹配的 catch,异常不会被静默丢弃。它会从 throw() 调用处继续向外抛,调用方应把这里当成一次普通的异常边界处理。另一个边界是状态:Fiber 尚未挂起、已经结束或当前正在运行时,不能随意调用 throw(),否则会得到 FiberError

PHP Fiber 状态方法与 Generator 调用栈边界对照图
图2:只有挂起态允许外部注入异常;Fiber 保留完整调用栈,而 Generator 通过 yield 暴露迭代边界。
start();

try {
    $fiber->throw(new LogicException('调用方终止本次工作'));
} catch (LogicException $e) {
    // 在这里统一记录、转换或交给上层请求处理器。
    echo '调用方收到:' . $e->getMessage(), PHP_EOL;
}
?>

Fiber 和 Generator 的选择边界

两者都能暂停执行,但模型不同。Fiber 有自己的完整调用栈,深层函数可以直接调用 Fiber::suspend(),调用方还可以在挂起时用 throw() 把异常送回暂停点。Generator 更像一个由 yield 驱动的迭代器,调用方通常围绕 current()send()throw() 组织数据流,函数返回类型也会变成 Generator

  • 需要暂停深层调用、由外部调度恢复或注入取消异常:优先考虑 Fiber。
  • 目标是逐项产出数据、遍历集合或实现惰性序列:Generator 更直观。
  • 无论选哪一个,都先检查当前状态,再决定调用 resume()throw() 还是读取最终返回值。

常见问题

Fiber::throw() 可以在 Fiber 尚未 start 时调用吗?

不可以。先用 start() 让 Fiber 运行到挂起点;尚未挂起时调用会触发状态错误。

异常被 Fiber catch 后还能在调用方 catch 到吗?

通常不能。被 Fiber 内部 catch 接住后,异常已在 Fiber 内消费;调用方应读取 getReturn() 得到处理结果。

为什么不能马上调用 getReturn()?

Fiber 必须已经终止并正常返回。仍在挂起或因异常终止时,getReturn() 都不是合适的读取入口。

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