
本文详解 Guzzle 中异步请求(getAsync)的正确使用方法,重点解决 Promise 未执行、回调不触发等常见问题,并提供可运行的并发控制示例。
本文详解 guzzle 中异步请求(`getasync`)的正确使用方法,重点解决 promise 未执行、回调不触发等常见问题,并提供可运行的并发控制示例。
在 Guzzle 中,调用 getAsync() 并不会立即发送 HTTP 请求——它仅返回一个 未激活的 Promise 对象。真正的网络请求只有在该 Promise 被“驱动”(如调用 wait()、then() 链式触发或被 EachPromise 管理并执行 promise()->wait())时才会发起。你原代码中 dd("NOT EXECUTED") 总是先执行,根本原因在于:生成器(yield)返回的 Iterator 并未被实际消费,导致所有 getAsync() 调用从未被构造,更谈不上执行。
✅ 正确做法:显式构建并等待 Promise 集合
以下是修复后的完整、可运行示例(基于 Guzzle 7+):
use GuzzleHttp\Client;
use GuzzleHttp\Promise\EachPromise;
use GuzzleHttp\Promise\Promise;
// 初始化客户端(推荐复用,避免重复创建)
$client = new Client([
'base_uri' => env('BADGEBOX_BASE_URL'),
'timeout' => 30,
]);
$page = $request->page;
$limit = $request->limit;
$pageStart = $page * $limit;
$startDate = $request->start_date;
$endDate = $request->end_date;
$query = $request->search ?? '';
$user_token = Crypt::decryptString($company->persistent_user_token);
$overallRecords = [];
// ✅ 关键修正1:直接构建数组而非生成器(避免 Iterator 延迟求值陷阱)
$promises = [];
for ($p = $pageStart; $p < $pageStart + $limit; $p++) {
$url = "/server/api/v1/company/overall?user_token=" . urlencode($user_token)
. "&limit=1&page={$p}&start_date={$startDate}&end_date={$endDate}&query=" . urlencode($query);
// 创建 Promise 并注册回调(注意:此时仍未发送请求)
$promises[] = $client->getAsync($url)->then(
function ($response) use (&$overallRecords) {
$body = (string) $response->getBody();
$data = json_decode($body, true);
if (is_array($data) && !empty($data)) {
foreach ($data as $record) {
$overallRecords[] = $record;
}
}
},
function ($reason) {
// ⚠️ 必须处理拒绝情况,否则异常会静默丢失
\Log::error('Async request failed: ' . $reason->getMessage());
}
);
}
// ✅ 关键修正2:使用 EachPromise 并显式 wait()
$eachPromise = new EachPromise($promises, [
'concurrency' => 10, // 同时最多 10 个并发请求
'fulfilled' => function ($value) { /* 可选:单个成功回调 */ },
'rejected' => function ($reason) { /* 可选:单个失败回调 */ },
]);
// ? 这行才是真正触发所有请求并阻塞等待完成
$eachPromise->promise()->wait();
// ✅ 此时 $overallRecords 已填充完毕
dd('ALL DONE', $overallRecords);? 核心要点总结
-
Promise 是惰性的:
getAsync()返回的是待执行的承诺,不是已发出的请求。必须通过wait()、Promise\all()或事件循环驱动才能执行。 -
避免生成器陷阱:PHP 的
yield在闭包中返回的是Generator,若未遍历(如未传给EachPromise构造函数),内部代码完全不执行。应直接构建array。 -
务必处理
rejected:网络超时、DNS 失败等均会导致 Promise 拒绝。忽略rejected回调将导致错误静默,难以调试。 -
合理设置
concurrency:过高可能触发目标服务限流;过低则失去并发优势。建议从5–10开始压测调整。 -
不要滥用
dd()在 Promise 回调中:dd()会终止整个进程,导致后续 Promise 无法继续处理。改用Log::info()或dump()(Laravel 9+)进行调试。
通过以上修正,你的异步请求将真正并发执行,"EXECUTED" 将按预期在每个响应到达时输出(请确保日志或调试工具能捕获异步上下文中的输出),而最终的 dd("NOT EXECUTED") 也会在全部请求完成后才触发。

















