想象一下这样的场景:你正在开发一个电商网站的订单处理系统。一个订单的创建可能需要同时调用多个第三方服务:支付网关、库存系统、物流接口,甚至还需要发送用户通知。如果这些操作都是同步执行的,那么整个订单处理过程会变得非常漫长。用户点击“提交订单”后,可能需要等待好几秒甚至更久,这无疑会严重影响用户体验。
我最初尝试的解决方案是简单地顺序调用这些服务。结果可想而知,用户抱怨页面响应慢,后台处理队列堆积。为了优化,我开始尝试使用一些非阻塞的库,但很快就遇到了另一个难题:“回调地狱”。当一个异步操作依赖于另一个异步操作的结果时,代码就会变成层层嵌套的回调函数,就像这样:
<pre class="brush:php;toolbar:false;">callPaymentApi(function ($paymentResult) {
if ($paymentResult->success) {
updateInventory(function ($inventoryResult) {
if ($inventoryResult->success) {
sendShippingRequest(function ($shippingResult) {
if ($shippingResult->success) {
sendNotification(function ($notificationResult) {
// ... 天哪,这代码还能看吗?
});
}
});
}
});
}
});这样的代码不仅难以阅读和理解,更糟糕的是,错误处理也变得异常复杂。任何一个环节出错,都需要在每一层回调中进行判断和处理,稍有不慎就可能导致错误被吞噬或者程序崩溃。我迫切需要一种更优雅、更具可维护性的方式来管理这些复杂的异步流程。
就在我为这些问题焦头烂额之际,我发现了
guzzlehttp/promises
安装 Guzzle Promises 非常简单,通过 Composer 一行命令即可:
立即学习“PHP免费学习笔记(深入)”;
<pre class="brush:php;toolbar:false;">composer require guzzlehttp/promises
Guzzle Promises 的核心在于
Promise
then()
一个 Promise 有三种状态:
你可以创建一个 Promise 对象,并在异步操作完成后手动
resolve()
reject()
<pre class="brush:php;toolbar:false;">use GuzzleHttp\Promise\Promise;
$promise = new Promise();
// 注册成功和失败的回调
$promise->then(
function ($value) {
echo "操作成功,得到值: " . $value . "\n";
},
function ($reason) {
echo "操作失败,原因: " . $reason . "\n";
}
);
// 模拟异步操作完成并成功
// 实际中,这可能在一个非阻塞的I/O操作完成后调用
$promise->resolve('订单已创建'); // 输出: 操作成功,得到值: 订单已创建
// 模拟异步操作完成并失败
// $promise->reject('支付失败'); // 如果调用这个,会输出: 操作失败,原因: 支付失败then()
then()
<pre class="brush:php;toolbar:false;">use GuzzleHttp\Promise\Promise;
$orderPromise = new Promise();
$orderPromise
->then(function ($orderId) {
echo "1. 订单创建成功,ID: " . $orderId . "\n";
// 假设这里调用支付API,并返回一个新的Promise
return new Promise(function ($resolve, $reject) use ($orderId) {
echo "2. 开始调用支付服务...\n";
// 模拟支付成功
sleep(1); // 模拟耗时操作
$resolve("支付成功 for order " . $orderId);
});
})
->then(function ($paymentResult) {
echo "3. " . $paymentResult . "\n";
// 假设这里更新库存,并返回一个普通值
echo "4. 更新库存中...\n";
sleep(0.5);
return "库存已更新";
})
->then(function ($inventoryResult) {
echo "5. " . $inventoryResult . "\n";
echo "6. 所有核心操作完成!\n";
})
->otherwise(function ($reason) { // 统一捕获链中任何环节的错误
echo "操作链中发生错误: " . $reason . "\n";
});
// 启动订单创建流程
$orderPromise->resolve('ORD12345');
// 注意:在实际异步环境中,你可能需要一个事件循环来驱动Promise的执行
// 但对于同步等待的场景,Promise会在wait()时自动驱动通过链式调用,原本嵌套的回调函数被扁平化,整个异步流程一目了然。每个
then()
wait()
尽管 Promise 的设计初衷是为了异步,但在某些场景下,你可能需要等待一个 Promise 完成并获取其结果,例如在脚本结束前确保所有任务都已完成。
GuzzleHttp\Promise
wait()
<pre class="brush:php;toolbar:false;">use GuzzleHttp\Promise\Promise;
$dataPromise = new Promise(function () use (&$dataPromise) {
echo "模拟从数据库加载数据...\n";
sleep(2); // 模拟数据库查询耗时
$dataPromise->resolve(['item1', 'item2']);
});
echo "程序继续执行,不等待数据加载。\n";
// 在需要数据时,同步等待Promise完成
$data = $dataPromise->wait(); // 此时程序会阻塞,直到$dataPromise被resolve或reject
echo "获取到的数据: " . implode(', ', $data) . "\n";wait()
cancel()
如果一个异步操作不再需要,你可以尝试使用
cancel()
<pre class="brush:php;toolbar:false;">use GuzzleHttp\Promise\Promise;
$longRunningTask = new Promise(
function () use (&$longRunningTask) {
// 模拟一个长时间运行的任务,最终会resolve
sleep(5);
$longRunningTask->resolve('任务完成');
},
function () {
echo "任务被取消了!\n";
// 这里可以执行清理操作
}
);
// 假设3秒后我们决定取消这个任务
sleep(3);
$longRunningTask->cancel(); // 如果任务未完成,会触发cancel回调
// 尝试等待,如果被取消,wait会抛出异常
try {
echo $longRunningTask->wait();
} catch (\Exception $e) {
echo "Wait抛出异常: " . $e->getMessage() . "\n";
}then(null, $onRejected)
otherwise()
wait()
guzzlehttp/promises
以上就是如何解决PHP异步任务的阻塞与回调地狱,GuzzlePromises助你构建高效优雅的应用的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号