
本文解析为何长耗时 ajax 请求会导致当前 chrome 会话完全卡顿、页面无法交互,并提供基于 promise 的非阻塞式异步处理方案,确保 ui 响应性不受后端慢请求影响。
本文解析为何长耗时 ajax 请求会导致当前 chrome 会话完全卡顿、页面无法交互,并提供基于 promise 的非阻塞式异步处理方案,确保 ui 响应性不受后端慢请求影响。
你遇到的现象——“Ajax 请求执行期间整个网站在当前 Chrome 标签页中完全无响应(空白、无法跳转、按钮失灵)”,并非 Ajax 本身阻塞了浏览器渲染线程,而是由一个常被忽视的底层机制导致:PHP 默认的 Session 文件锁(Session File Locking)。
当你的 /controller.php 启用了 session_start()(例如用于用户登录态、CSRF token 或临时缓存),PHP 会以独占方式打开并锁定对应的 session 文件(如 /tmp/sess_xxx)。而同一浏览器会话(同域、同 Cookie)的所有后续 HTTP 请求(包括其他页面加载、AJAX、图片请求等)都会因等待该锁释放而排队阻塞——这就是为什么只有「当前 Chrome 窗口/标签页」卡死,而手机或其他浏览器窗口完全正常的原因。
⚠️ 注意:这不是 jQuery 或 Ajax 的 Bug,也不是前端代码写法问题;而是 PHP 服务端默认行为与浏览器同源会话复用机制共同导致的典型瓶颈。
✅ 正确解决方案:释放 Session 锁
在 /controller.php 开头,一旦完成 session 数据读取,立即调用 session_write_close():
<?php
session_start();
// ✅ 立即释放 session 文件锁,允许其他请求并发执行
if (isset($_SESSION['user_id'])) {
$userId = $_SESSION['user_id'];
}
session_write_close(); // ? 关键!必须在此处调用
// 后续业务逻辑(如 cURL 请求)可安全执行,不再阻塞其他请求
$action = $_GET['action'] ?? '';
if ($action === 'loadPageContent') {
// 模拟耗时操作(如多路 cURL)
$htmlDatas = file_get_contents('https://example.com/api/data'); // 替换为实际逻辑
$date_scrapping = date('c');
echo json_encode([
'status' => true,
'htmlDatas' => $htmlDatas,
'date_scrapping' => $date_scrapping
]);
}? 前端优化:使用 Promise 封装,提升可维护性(非必需但推荐)
虽然释放 session 锁已解决根本问题,但将 Ajax 封装为 Promise 可显著增强代码可读性与错误处理能力,并天然支持 async/await:
function ajaxCall() {
return new Promise((resolve) => {
$.ajax({
url: '/controller.php',
type: 'GET',
dataType: 'json',
data: { action: 'loadPageContent' },
success: function (retour) {
localStorage.removeItem('myhtml');
if (retour.status && retour.htmlDatas) {
storedDatas.posts = retour.htmlDatas;
storedDatas.date_scrapping = retour.date_scrapping;
localStorage.setItem('myhtml', JSON.stringify(storedDatas));
$('#container').html(retour.htmlDatas);
resolve(true);
} else {
alert('数据加载失败');
resolve(false);
}
},
error: function () {
alert('网络请求异常');
resolve(false);
}
});
});
}
// 使用示例(可与其他逻辑并行执行)
async function loadContent() {
// 显示加载状态(提升用户体验)
$('#container').html('<div class="loading">加载中...</div>');
const success = await ajaxCall();
if (!success) {
$('#container').html('<div class="error">加载失败,请重试</div>');
}
}? 补充建议
- 避免在长耗时脚本中写入 session:若无需更新 session 数据,全程不调用 session_start() 最佳;
- 考虑升级至无锁 session 驱动:如 Redis 或 Memcached 存储 session,彻底规避文件锁;
- 前端增加超时与防重复提交:防止用户多次触发导致服务端积压;
- 服务端启用 ignore_user_abort(true)(谨慎使用):确保即使用户关闭页面,后台任务仍继续执行(适用于纯后台作业)。
通过 session_write_close() 主动释放锁 + Promise 化前端调用,即可在不改动架构的前提下,彻底解决“Ajax 卡死整个页面”的问题,兼顾性能、兼容性与可维护性。

















