使用Fetch API发起多请求未异步执行 请求pending排队阻塞问题
问题成因
你观测到的9个请求总耗时约1分钟、控制台全部显示pending、没有实现异步并发效果,核心原因有两个:
- 后端PHP Session排他锁阻塞(核心原因):PHP默认以文件形式存储Session,调用
session_start()时会自动给当前用户的Session文件加排他锁,直到请求执行结束才会释放。锁持有期间,同个客户端发起的所有携带相同Session ID的请求都会在后端排队等待锁,根本不会进入业务逻辑执行。这种完全串行的执行模式下,9个单耗时6秒的请求总时长刚好是9*6=54秒,和你观测到的1分钟耗时完全吻合。如果只是浏览器端的并发限制,总耗时应该在12秒左右(同域名HTTP/1.1默认最大6个并发,分两批执行),不会达到1分钟级别。 - 浏览器同域名并发连接限制:HTTP/1.1协议下,浏览器针对单个域名默认最多同时建立6个TCP连接发送请求,超出数量的请求会在浏览器端排队等待连接释放,即使解决了后端锁问题,9个请求也会分两批发送,不会做到完全并行。
代码存在的显性问题
- 变量拼写错误:代码中fetch配置的
body字段传入的是foormData,和函数入参formData相比多写了一个字母o,会直接触发引用错误,属于笔误。 - 无意义的async标记:给
$.each的回调函数加了async关键字,但回调内部没有任何await逻辑,这个写法没有实际作用,还会导致fetch抛出的请求异常无法被外层捕获。
修复方案
- 后端锁问题修复:在
/api/execute.php中,读取完Session内需要的数据后,立刻调用session_write_close()手动释放Session锁,不要等请求全部执行完再释放,这样后续请求就不会在后端排队:
<?php session_start(); // 读取业务需要的Session数据 $currentUser = $_SESSION['user']; // 读完立刻释放锁,不阻塞其他同Session请求 session_write_close(); // 后续执行耗时的设备指令下发逻辑
- 前端代码修正:修复笔误,替换无意义的循环写法,加上异常捕获:
async function getExecuteOutput(devices, formData) { const requestList = devices.map(() => { return fetch('/api/execute.php', { method: 'POST', body: formData // 修正之前的foormData拼写错误 }) .then(resp => resp.json()) .then(res => console.log('指令执行结果', res)) .catch(err => console.error('指令下发失败', err)) }) // 如果需要等所有指令都下发完成再执行后续逻辑,放开下面这行注释即可 // await Promise.all(requestList) }
- 可选优化:给服务端开启HTTP/2支持,利用多路复用特性突破HTTP/1.1下的同域名并发连接数限制,进一步减少请求排队概率。
现象参考截图


内容的提问来源于stack exchange,提问作者Mentle Storier
相关产品推荐
相关产品推荐

