Laravel 如何在向 AJAX 请求返回响应后继续执行耗时函数
Laravel 场景下「先返回AJAX响应,再执行后端耗时逻辑」实现方案
核心实现思路
不需要套用原生PHP场景下的echo + 缓冲区冲刷野路子,完全可以在保留Laravel控制器return响应写法的前提下,通过两种成熟方案实现需求,前端AJAX请求完全不需要改动现有代码。
方案1:队列异步执行(生产环境首选,稳定可靠)
- 实现原理:控制器正常返回JSON响应给前端,把需要执行的「第三方API调用、access token刷新」这类耗时逻辑推送到独立队列,由单独的队列进程后台执行,完全不占用当前请求的响应时间。
- 落地步骤:
- 新建队列任务类,把原有cURL请求、token判断刷新的逻辑全部迁移到任务类的处理方法中:
// app/Jobs/SendThirdPartyPurchaseNotify.php <?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; class SendThirdPartyPurchaseNotify implements ShouldQueue { use Dispatchable, Queueable; // 传入任务需要的业务参数(比如订单ID、用户标识) public function __construct(protected int $orderId) {} // 原有cURL调用、token刷新逻辑全部写在这里 public function handle() { // 原有cURL请求代码直接复用即可 } }- 调整原有控制器逻辑,正常返回响应的同时派发队列任务:
public function completePurchase(Request $request) { // 原有订单校验、业务处理逻辑 $response = [ 'success' => $业务处理结果, 'message' => '购买完成' ]; // 提前准备好要返回给前端的JSON响应 $jsonResponse = response()->json( $response, $response['success'] ? 200 : 400 ); // 派发异步任务,无需等待任务执行完成 SendThirdPartyPurchaseNotify::dispatch($orderId); // 直接返回响应,前端会立刻收到结果 return $jsonResponse; } - 注意事项:提前配置好队列驱动(支持database、Redis等多种类型),启动队列worker进程,框架会自动处理任务重试、执行日志、失败兜底,比裸写的异步进程可控性更强。
方案2:响应完成回调执行(低并发场景临时方案)
- 实现原理:利用Laravel内置的
terminate回调机制,等框架把响应内容全部推送到浏览器、完成响应流程后,再执行业务逻辑,完全不需要改动原有控制器的return写法。 - 落地代码:
public function completePurchase(Request $request) { // 原有业务处理逻辑 $response = [/* 要返回给前端的业务数据 */]; $jsonResponse = response()->json($response, 200); // 注册响应完成后的执行逻辑,这部分代码会在响应推给浏览器后才运行 $jsonResponse->terminate(function () use ($request) { // 把原有的cURL第三方请求、token刷新逻辑放到这里 $this->callThirdPartyApi($request->all()); }); return $jsonResponse; } - 注意事项:该方案不需要额外启动队列进程,但PHP-FPM会等回调执行完成后才会释放进程,高并发场景下容易造成进程池阻塞,仅适合低并发的临时需求场景,生产环境长期使用优先选队列方案。
常见误区澄清
- 不要硬套原生PHP的缓冲区冲刷方案:在Laravel控制器里直接写
ob_start() + echo() + ob_end_flush()的写法会绕过Laravel的响应处理流程,导致响应头错乱、中间件逻辑中断、CSRF校验状态混乱,完全没必要——Laravel本身已经封装好了响应回调、队列等机制来解决这类问题。 - 前端的JavaScript Promise/异步能力无法解决这个问题:前端的异步逻辑是控制浏览器侧的代码执行时序,如果后端没有在返回响应前就把耗时逻辑剥离,就算前端用Promise包裹AJAX请求,也还是会等后端处理完所有逻辑、返回响应后才会触发
done回调,解决不了用户等待的问题。 - 原有cURL代码完全不需要做改动:不管是把逻辑搬到队列任务里,还是放到
terminate回调里,之前写的cURL请求、token过期判断的代码可以直接复用,不需要调整语法逻辑。
内容的提问来源于stack exchange,提问作者Levy Barbosa
相关产品推荐
相关产品推荐

