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

非jQuery环境下Ajax调PHP执行耗时Python脚本无返回的解决办法

问题解决思路与方案

核心问题分析

  • 同步Ajax的浏览器超时:同步XMLHttpRequest会被浏览器强制设置超时,长时LLM任务触发超时后,浏览器提前终止请求,导致返回空结果。
  • PHP exec的局限性:exec仅捕获Python脚本的最后一行输出,且如果PHP的max_execution_time小于Python任务耗时,会直接中断脚本执行;另外Python默认缓冲输出,未刷新的内容无法被PHP捕获。
  • 同步请求的阻塞特性:同步请求会锁住浏览器界面,用户体验极差,且现代浏览器对同步XHR的限制越来越严格。

轻量解决方案(无需jQuery/页面重构)

方案一:异步Ajax + 后台任务 + 轮询(推荐)

把长任务丢到PHP后台执行,前端通过轮询获取结果,完全不阻塞页面。

1. 修改PHP调用脚本(myPhpPythonCaller.php)

<?php
// 生成唯一任务ID
$taskId = uniqid('llm_task_', true);
// 临时文件路径,存储Python输出
$outputFile = "/tmp/{$taskId}.txt";
// 后台执行Python脚本,输出写入临时文件,同时捕获错误
$pythonCmd = "pathToMyPythonEnv/.venv/bin/python3 pathToMyPythonScript.py > {$outputFile} 2>&1 &";
exec($pythonCmd);
// 返回任务ID给前端
echo $taskId;
?>

2. 新增PHP轮询脚本(checkTaskResult.php)

<?php
$taskId = $_POST['taskId'];
$outputFile = "/tmp/{$taskId}.txt";
$result = [
    'completed' => false,
    'result' => ''
];
if (file_exists($outputFile)) {
    $content = file_get_contents($outputFile);
    // 检查Python脚本是否执行完成(通过末尾的特定标记判断)
    if (strpos($content, '__TASK_DONE__') !== false) {
        $result['completed'] = true;
        $result['result'] = str_replace('__TASK_DONE__', '', $content);
        // 可选:删除临时文件释放空间
        unlink($outputFile);
    } else {
        $result['result'] = $content;
    }
}
echo json_encode($result);
?>

3. 修改Python脚本,添加完成标记并刷新输出

from library import functionx
import sys

res = functionx()
# 输出结果并添加完成标记
print(res)
print("__TASK_DONE__")
# 强制刷新输出缓冲区,确保内容立即写入文件
sys.stdout.flush()

4. 前端修改为异步请求+轮询

// 全局变量存储轮询定时器
let taskPollTimer = null;

function startLLMTask() {
    // 发起异步请求启动任务
    ajaxRequestReturn("myPhpPythonCaller.php", vars="", isAsync=true, setHeader=false, jsonParsed=false);
    // 等待请求完成后获取任务ID,开始轮询
    setTimeout(() => {
        const taskId = ajaxResponseText;
        taskPollTimer = setInterval(() => {
            // 构造轮询参数
            const pollVars = `taskId=${encodeURIComponent(taskId)}`;
            ajaxRequestReturn("checkTaskResult.php", pollVars, isAsync=true, setHeader=true, jsonParsed=true);
            // 检查任务状态
            if (ajaxResponseText.completed) {
                clearInterval(taskPollTimer);
                // 处理最终结果
                console.log("任务完成:", ajaxResponseText.result);
                // TODO: 将结果渲染到页面对应位置
            } else {
                // 可选:显示任务中间进度
                console.log("任务进行中:", ajaxResponseText.result);
            }
        }, 2000); // 每2秒轮询一次
    }, 500);
}

// 页面事件处理器调用
startLLMTask();

方案二:调整超时与输出缓冲(仅短时长任务可用,不推荐)

如果一定要保留同步请求模式,需要调整PHP和Python的参数,但仍可能受浏览器超时限制。

1. 修改PHP脚本,延长超时并捕获完整输出

<?php
// 延长PHP执行超时时间(单位:秒)
set_time_limit(300); // 设置为5分钟
// 使用passthru替代exec,捕获Python所有输出
// -u 参数让Python禁用输出缓冲,实时输出内容
passthru("pathToMyPythonEnv/.venv/bin/python3 -u pathToMyPythonScript.py");
?>

2. 前端同步请求调整(可选)

部分浏览器支持给同步XHR设置超时,但兼容性差,建议优先用方案一:

// 在ajaxRequestReturn函数中添加超时设置
req.timeout = 300000; // 5分钟
req.ontimeout = function() {
    ajaxResponseText = "任务执行超时";
};

注意事项

  • 临时文件权限:确保PHP有权限读写/tmp目录(或自定义的临时目录)。
  • 任务清理:可定时清理过期的临时文件,避免磁盘空间浪费。
  • 异步请求回调优化:上面示例用定时器处理异步结果,更严谨的方式是给ajaxRequestReturn添加回调参数,避免依赖全局变量的异步赋值问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 22:53:14