使用setInterval每2秒轮询POST请求更新out.rule时触发json解析报错
轮询报body stream already read的问题排查与修复
问题成因
- 初始化阶段仅执行1次
fetch()调用,将返回的Promise赋值给fetchURL,后续所有定时逻辑都在复用这个单次请求的结果。 - Fetch API的Response对象采用流读取机制,响应体流一旦被
.json()、.text()等方法读取,就会被完全消耗,无法二次读取。每2秒对同一个已经被读过的Response重复调用.json(),就会直接触发body stream already read的报错。 - 这种写法本质上没有按间隔发新的请求,永远只能拿到第一次请求的响应结果,自然无法获取服务端最新的
out.rule数据。 - 附带逻辑bug:原代码里的
console.log('Error')写在setInterval的同步执行分支,和请求的catch逻辑完全分离,不管请求成功失败都会打错误日志,完全起不到错误监控的作用。
修复实现
按照以下逻辑调整:
- 把完整的POST请求逻辑封装为独立函数,每次定时触发时重新发起请求,不复用已消耗的响应对象。
- 增加请求状态锁,避免上一次请求耗时超过2秒的轮询间隔时,出现请求堆叠、旧响应覆盖新数据的竞态问题。
- 将错误捕获、日志打印移入请求流程的catch分支,精准捕获请求、数据解析阶段的异常。
修复后的参考代码:
// 封装独立的规则数据拉取逻辑,每次调用都会重新发起完整请求 async function fetchRuleData() { const response = await fetch("myURL", { headers: { "accept": "application/json, text/plain, */*", "accept-language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", "content-type": "application/json;charset=UTF-8", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "cross-site" }, referrerPolicy: "strict-origin-when-cross-origin", body: "{myBody}", method: "POST", mode: "cors", credentials: "omit" }); return response.json(); } // 简单请求锁,避免请求堆叠 let isPolling = false; async function executePoll() { // 上一次请求未完成就跳过本次轮询 if (isPolling) return; isPolling = true; try { const out = await fetchRuleData(); // 匹配规则时执行业务逻辑 if (out.rule === 'admin') { // 业务逻辑占位 } } catch (err) { console.error('轮询拉取规则数据失败:', err); } finally { // 无论请求成功失败,释放锁 isPolling = false; } } // 启动2000ms间隔的轮询 const refreshIntervalId = setInterval(executePoll, 2000);
内容的提问来源于stack exchange,提问作者kevin
相关产品推荐
相关产品推荐

