PHP的异步编程该怎么选择

发布时间:2026/9/6 1:49:57

PHP的异步编程该怎么选择
PHP 的传统执行模型是同步的这意味着代码按照语句出现的顺序逐条执行。这本身并非问题因为同步思维往往更为简单。当要求 PHP 开发者实现 SQL 分页展示时他们通常会先执行一条统计总数的查询再执行第二条查询获取当前页的数据。总记录数对于生成分页链接首页、下一页、末页等是必需的。当 SQL 服务器处理第一条计数查询时PHP 服务器处于等待状态收到响应后才执行第二条查询。当然存在一次性获取两种信息的方法但那不是本文的主题请保持专注。从这个分页示例中我们可以看到潜在的优化空间在 SQL 服务器处理第一条查询的同时启动第二条查询。但要注意在拿到计数结果之前我们不会显示分页链接因此即使计数查询先完成也需要等待另一条查询的结果。由此可见异步操作的管理不仅限于并行执行任务还包括管理响应的处理顺序。存在许多需要异步执行代码的场景这通常与 I/O 操作相关HTTP 请求、数据库访问、文件读写或启动外部进程。PHP 是异步的吗要判断 PHP 是否异步首先需要理解异步的含义。异步指的是不同时发生。当某项操作耗时时与其等待完成不如先去做其他事情等操作完成后再回来继续。因此异步的核心在于操作是非阻塞的。人们常常混淆异步和并行。打个比方异步如同一位厨师将锅接满水放在灶台上开火趁水烧开的工夫去切蔬菜。等蔬菜切好、水也烧开就开始烹饪。并行则是两位厨师一位切蔬菜的同时另一位负责烧水。蔬菜切好、水也烧开后由第一位厨师负责烹饪。并行节省了时间因为切蔬菜与烧水准备是同时进行的。但两种模式下水烧开的过程中都可以去做其他事情。具体而言我们的厨师就是机器的 CPU/GPU。PHP 的异步能力从 2002 年 PHP 4.3 发布起一项重要功能被引入Streams。通过 stream_set_blocking() 和 stream_select() 函数PHP 进入了异步编程时代。注意这段示例代码刻意简化未处理错误等情况。在 usleep(1000) 的位置可以执行其他操作比如读取另一个文件甚至向其他服务器发起 HTTP 请求。不过如果你的文件系统很快可能不会进入等待时间。这种技术更适合处理慢速文件系统或其他类型的 I/O 操作。23 年前 PHP 就已支持异步编程然而几年前人们还说 PHP 不是异步语言为什么因为实现异步不仅仅是启动非阻塞处理还需要有机制来管理这些等待时间。这就引入了协程的概念。协程是一种可以被挂起、之后恢复的函数。PHP 8.1 的发布标志着 PHP 向异步编程迈出了重要一步引入了 Fiber 作为真正的协程技术基础你会发现代码与使用生成器时几乎没什么变化。虽然 PHP 从 4.3 版本就具备底层异步能力但 PHP 8.1 引入的 Fiber 标志着一个转折点。Fiber 提供了原生且强大的异步编程工具使其变得更加自然。Event Loop既然我们已经知道如何中断协程并执行非阻塞处理接下来需要管理多个并行任务因为单个异步处理的意义不大。谈到并行人们常会想到线程——线程提供进程间的自然隔离并能利用多核 CPU这对计算密集型任务非常有吸引力。然而并行、特别是多线程的实现更为复杂调试更困难还存在死锁和内存并发访问的风险。正是出于这些原因Web 领域更倾向于使用另一种模式EventLoop。Web 场景的特点是并发连接数可能非常高。EventLoop 是一个无限循环它监听事件队列如结果到达并以串行方式逐个处理。我们将待处理的任务加入这个队列然后启动循环。问题是如何告知 EventLoop 如何处理任务的结果很简单我们指定一个回调函数当结果可用时 EventLoop 会调用它。注意下面代码中的 EventLoop 是虚构的但代表了大多数 EventLoop 的工作方式。$loop EventLoop::get();$loop-addReadStream(file.txt, function(string $data) {echo 读取到的数据{$data};});echo 启动 EventLoop\n;$loop-run();这段代码的预期输出启动 EventLoop读取到的数据file.txt 的内容同时读取两个文件的情况$loop EventLoop::get();$loop-addReadStream(/dev/cdrom/file1.txt, function(string $data) {echo 数据 1 已读取{$data};});$loop-addReadStream(/dev/fb0/file2.txt, function(string $data) {echo 数据 2 已读取{$data};});echo 启动 EventLoop\n;$loop-run();根据存储介质的性能输出可能是启动 EventLoop数据 2 已读取软盘数据数据 1 已读取光盘数据Promise当需要链式执行异步操作时就会陷入回调地狱或末日金字塔回调函数层层嵌套。$loop EventLoop::get();$loop-addReadStream(file.txt, function(string $data) {EventLoop::get()-defer(function() use ($data) {return compressData($data);}, function ($compressedData) {EventLoop::get()-addWriteStream(http://foo,$compressedData,function (Response $response) {echo 数据已发送\n;});});});echo 启动 EventLoop\n;$loop-run();如果再加上错误处理代码会更加复杂难读。为了改善可读性和更好地管理异步Promise承诺的概念值得考虑。Promise 的概念于 80 年代在 Multilisp 等语言中引入但真正流行是在 2009 年Dojo、Q、jQuery.Deferred 等 JavaScript 库率先实现了它。Promise 是什么它是一个包含处理结果当前或未来的对象。打个比方我不会立即给你处理结果但我承诺稍后会在这个对象里给你。示例代码$promise new Promise(function ($resolve, $reject) {echo 启动 Promise\n;$resolve(Hello, world!);});运行这段代码会看到 启动 Promise但 Hello, world! 在哪里为什么要调用 $resolve()实际上需要使用 then() 方法配合回调函数$promise new Promise(function ($resolve, $reject) {echo 启动 Promise\n;$resolve(Hello, world!);});$promise-then(function ($value) {echo Promise 结果$value\n;});输出启动 PromisePromise 结果Hello, world!如果 Promise 没有被解决resolve什么都不会发生只会显示启动信息。具体来说当 Promise 被解决时then() 中的回调会被执行。这种情况可能发生在 Promise 内部包含协程时——协程经过长时间处理收到结果后调用 $resolve()。配合 EventLoop 的完整示例$loop EventLoop::get();$promise new Promise(function ($resolve, $reject) use ($loop) {echo 启动 Promise\n;$loop-addTimer(1, function () use ($resolve) {echo 解决 Promise\n;$resolve(Hello, World!);});});$promise-then(function ($value) {echo 结果$value\n;});$loop-run();这段代码使用异步定时器在 1 秒后解决 Promise。输出启动 Promise解决 Promise结果Hello, World!Promise 的价值体现在哪里回到回调地狱的问题。使用 Promise 后代码可以这样写readFileAsync(file.txt)-then(function ($data) {return compressDataAsync($data);})-then(function ($compressedData) {return sendDataAsync(http://foo, $compressedData);})-catch(function ($error) {echo 错误{$error}\n;});readFileAsync() 返回一个使用 EventLoop 的 Promise在获得结果时解决。compressDataAsync() 和 sendDataAsync() 同样返回 Promise。catch() 用于处理链中任何环节的错误。现在我们不再是嵌套回调而是回调链。你也可以在回调中返回值这个值会被转换为立即解决的 Promise。如果不返回任何内容相当于返回一个值为 NULL 的已解决 Promise。如果需要在各阶段处理错误then() 方法接受第二个参数作为拒绝错误时的回调readFileAsync(file.txt)-then(function ($data) {return compressDataAsync($data);},function ($error) {echo 文件读取错误{$error}\n;})-then(function ($compressedData) {return sendDataAsync(http://foo, $compressedData);})-catch(function ($error) {echo 错误{$error}\n;});需要注意的是如果错误回调返回了值或没有 return后续的 then() 会收到一个已解决的 Promise。因此需要返回一个错误状态的 Promise 或抛出异常。这是 then(onResolve, onReject) 中处理错误的常见陷阱之一——需要在后续所有 then() 中处理错误。上面的代码中sendDataAsync() 会收到包含 NULL 的 $compressedData。包选型建议在 Packagist 上搜索 promise 会发现有 4 个包较为突出。Guzzle/promises 和 php-http/promiseguzzle/promises 的下载量遥遥领先很大程度上是因为它被流行的 HTTP 客户端 guzzle/guzzle 直接使用。如果你已经在使用 Guzzle可能无需选择其他包因为它已经相当完善。但 Guzzle/Promises 最初是为处理异步 HTTP 请求设计的使用内部不暴露的 EventLoop这使得集成其他类型的 I/O如 Mysqli 异步查询或进程更加困难。php-http/promise 情况类似同样专注于 HTTP 请求。ReactPHP 和 Amp剩下的两个重要选择是 react/promise 和 amphp/amp。ReactPHP 提供了简单且高性能的 JavaScript Promises/A 标准实现Promise 最初是 JavaScript 语言中涌现的标准没告诉过你吧。Amp 则没有完全实现 Promise3.0 版本中没有 then()但它实现了另一种机制——Futures设计用于在基于生成器或 Fiber 的协程中通过 await() 等待。因此一边是 Promise 链式管理另一边是面向协程的管理。如果你用过 JavaScript 的 PromiseReactPHP 可能更容易上手否则 Amp 的协程方式代码可读性更好更接近我们习惯的同步 PHP 写法。但无论选择 ReactPHP 还是 Amp都需要 EventLoop。ReactPHP 提供 react/event-loop 包Amp 推荐使用 revolt/event-loop——这是 Amp 团队发起的项目旨在围绕现代事件循环标准统一 PHP 异步生态。Revolt 可通过适配器与 ReactPHP 互操作。

相关新闻

java——顺序表ArrayList与链表LinkedList

java——顺序表ArrayList与链表LinkedList

2026/9/6 1:49:57

一.引言 通过本篇博客,学者将深刻认识到顺序表ArrayList与链表LinkedList的区别和使用方法,文章将通过对概念的深度解析,用通俗易懂的白话讲清了两者的关系和使用场景。 二.目录 1,线性表 2.顺序表 3.何为ArrayList? 4.Arra…

本地网关:解锁 Codex 与 Claude Code 的多模型自由路由

本地网关:解锁 Codex 与 Claude Code 的多模型自由路由

2026/9/6 1:49:57

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

杭州有哪些专业代理SC证的机构?选对少走三个

杭州有哪些专业代理SC证的机构?选对少走三个

2026/9/6 1:49:57

杭州有哪些专业代理SC证的机构?选对少走三个月弯路你有没有经历过这种“卡壳感”:车间、人、设备都到位了,产品也能稳定出货,可一提到SC证(食品生产许可证)就心里发虚——材料一大堆、条款看不懂、现场核查…

用 Python 做起名网站竞争对手 SEO 分析与差距分析:从关键词到内容的全方位对标

用 Python 做起名网站竞争对手 SEO 分析与差距分析:从关键词到内容的全方位对标

2026/9/6 7:10:11

一、前言 在起名服务这个竞争激烈的细分市场中,SEO 是获取自然流量的核心渠道。然而,很多起名网站的 SEO 优化都是 “闭门造车”,只关注自己的网站,不了解竞争对手在做什么,导致优化方向偏离、资源浪费、效果不佳。笔…

多项式回归预测中国人寿保费 —— 从代码理解 PolynomialFeatures 与线性回归的配合

多项式回归预测中国人寿保费 —— 从代码理解 PolynomialFeatures 与线性回归的配合

2026/9/6 7:10:11

一、项目整体流程读取Excel数据→ KDE可视化做特征选择(删掉不重要的特征)→ BMI离散化(连续值转分类)→ One-Hot编码(文本转数值)→ PolynomialFeatures多项式升维(造交叉特征)→ 拆…

东方博宜OJ 1477:字符串出现次数 ← BF算法

东方博宜OJ 1477:字符串出现次数 ← BF算法

2026/9/6 7:10:11

【题目来源】 https://oj.czos.cn/p/1477 【题目描述】 一个字符串中任意个连续的字符组成的子序列为该字符串的子串。给定子串 s1 和它的一个字符串 s2,求 s1 在 s2 中出现的次数。 【输入格式】 第一行,表示字符串 s1,第二行表示字符串 s…

解决报错 Error: Missing optional dependency @openai/codex-linux-arm64

解决报错 Error: Missing optional dependency @openai/codex-linux-arm64

2026/9/6 7:10:11

解决报错 Error: Missing optional dependency openai/codex-linux-arm64 一图看懂:问题复现: uname -a Linux hostname-2pbfv.foreman.pxe 5.10.0-282.0.0.184.oe2203sp3.aarch64 #1 SMP Wed Sep 17 13:32:56 CST 2025 aarch64 aarch64 aarch64 GNU/Lin…

如何通过AI营销机器人提升获客转化率?

如何通过AI营销机器人提升获客转化率?

2026/9/6 7:10:11

AI营销机器人是现代企业提高获客转化率的重要工具。它们通过智能自动化,使企业能够更高效地优化客户互动和实施精准营销。机器人能够根据用户的数据和行为特点,快速生成个性化内容,进而加强客户的参与度和购买意愿。另外,这些工具…

Vibe Coding实战:自然语言驱动AI编程的技巧与避坑指南

Vibe Coding实战:自然语言驱动AI编程的技巧与避坑指南

2026/9/6 7:00:11

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/6 1:19:56

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/6 1:19:56

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/6 1:19:56

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/6 1:19:56

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/6 1:19:56

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/6 1:19:56

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

远程协作的工作台整理

远程协作的工作台整理

2026/9/3 6:56:24

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/4 7:42:10

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/5 23:14:13

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…