You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP脚本兼容Cron Job与AJAX调用:AJAX触发时页面冻结

解决AJAX调用PHP长脚本导致Web应用冻结的方案

问题核心

你的PHP脚本通过sleep()逐个处理记录,导致脚本执行时间过长;同时PHP默认的会话锁机制会阻塞其他页面请求,最终让整个Web应用看起来冻结。Cron模式正常是因为它不涉及前端会话的阻塞问题。


解决方案

1. 立即释放会话锁(快速修复)

PHP在开启Session后会独占session文件锁,直到脚本结束才释放。如果AJAX请求长时间占用锁,其他页面请求会被阻塞。在脚本完成Session相关操作后,主动关闭Session:

// 完成Session读取/写入后立即关闭
session_write_close();

// 后续的数据库查询、curl请求、sleep操作都不会再占用Session锁

2. 改用异步任务队列(根本解决)

长任务不适合通过AJAX同步执行,应该把任务丢进队列,让后台Cron或Worker进程处理,前端只负责提交任务和查询状态:

  • 后端修改:新增一个仅负责任务入队的接口
public function queueDataTask() : array {
    // 1. 验证请求合法性
    // 2. 将任务参数写入队列(比如数据库表task_queue)
    $taskId = $database->insertTask([
        'type' => $_POST['type'],
        'params' => $_POST['params'],
        'status' => 'pending'
    ]);
    // 3. 立即返回响应,不需要等待任务完成
    return [
        'success' => true,
        'task_id' => $taskId,
        'msg' => '任务已提交,后台正在处理'
    ];
}
  • 前端修改:提交任务后通过轮询获取处理状态
function apiRequestHandler(type, params) {
    $.ajax({
        type: 'POST',
        url: '/queue-task', // 新的入队接口
        data: { type, params },
        dataType: 'json',
        success: (data) => {
            if (data.success) {
                // 启动轮询检查任务状态
                const statusTimer = setInterval(() => {
                    $.get(`/task-status?id=${data.task_id}`, (statusData) => {
                        if (statusData.status === 'completed') {
                            alert('任务处理完成');
                            clearInterval(statusTimer);
                        } else if (statusData.status === 'failed') {
                            alert('任务处理失败');
                            clearInterval(statusTimer);
                        }
                    });
                }, 5000); // 每5秒查询一次
            }
        }
    });
}
  • 后台处理:复用现有Cron Job,每次执行时从task_queue中取出待处理任务,用curl_multi_init()批量处理。

3. 优化同步脚本(临时过渡方案)

如果暂时无法改队列,把逐个处理+sleep改成并发请求,大幅缩短脚本执行时间:

public function getAndHandleData() : array {
    $data = $database->getData();
    $mh = curl_multi_init();
    $curlHandles = [];

    // 批量创建curl句柄
    foreach ($data as $item) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $item['service_url']);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $item['request_data']);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $curlHandles[] = $ch;
        curl_multi_add_handle($mh, $ch);
    }

    // 执行并发请求
    $running = null;
    do {
        curl_multi_exec($mh, $running);
        curl_multi_select($mh);
    } while ($running > 0);

    // 处理所有响应
    $results = [];
    foreach ($curlHandles as $ch) {
        $response = curl_multi_getcontent($ch);
        $results[] = $this->processResponse($response);
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }
    curl_multi_close($mh);

    return $results;
}

这种方式不需要sleep(),所有请求并行处理,脚本执行时间等于单个请求的最长耗时,而非累加时间。


内容的提问来源于stack exchange,提问作者GiLA3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 02:02:01