如何通过单次AJAX调用接收PHP端多个json_encode返回数据
单次AJAX接收PHP循环逐次输出JSON响应的实现方案
默认逻辑下浏览器会等待服务端脚本完全执行完成、返回完整响应后才触发AJAX的成功回调,要实现单次请求实时接收分段输出的内容,需要同时调整PHP端的输出缓冲配置、前端AJAX的流进度监听逻辑,无需发起多次请求。
PHP端(insert.php)调整
核心是关闭所有层级的输出缓冲,每次输出后强制将内容推送到客户端,避免内容被攒到请求结束后统一返回。
调整后的完整代码:
<?php // 关闭所有已存在的输出缓冲层级 while (ob_get_level()) { ob_end_clean(); } // 设置响应头,关闭服务端缓冲、禁用缓存 header('Content-Type: text/plain; charset=utf-8'); header('X-Accel-Buffering: no'); // 关闭Nginx反向代理缓冲 header('Cache-Control: no-cache'); // 立即输出响应头 ob_flush(); flush(); $num1 = $_POST['num1']; $num2 = $_POST['num2']; $post_data = $_POST; $id = []; for ($i = 1; $i <= 10; $i++) { $id['id'] = $i; $data = array_merge($id, $post_data); // 输出JSON后追加换行符,作为前端分段解析的分隔标记 echo json_encode($data) . PHP_EOL; // 强制将当前缓冲区内容推送到客户端 ob_flush(); flush(); sleep(1); }
注意事项:
- 不要在脚本中主动调用
ob_start()开启输出缓冲,否则会导致刷出失效 - 如果使用Apache服务,可额外添加响应头
header('Transfer-Encoding: chunked');确保分块传输生效 - 必须关闭对应接口路径的gzip/brotli等输出压缩,压缩模块会攒够固定体量的内容才会输出,导致实时推送失效
前端AJAX逻辑调整
jQuery封装的$.ajax默认不会暴露分段响应的处理接口,需要通过原生XMLHttpRequest的onprogress事件监听流传输进度,手动解析每一段接收到的完整JSON。注意不要配置dataType: 'json',因为整个响应是多段JSON拼接的文本流,不是单个合法JSON对象。
调整后的完整前端代码:
$(function() { $("form").submit(function(e) { e.preventDefault(); // 记录已处理的响应文本位置,避免重复解析 let lastProcessPos = 0; $.ajax({ url: "insert.php", method: "POST", data: $("form").serializeArray(), // 自定义原生XHR对象,绑定进度监听 xhr: function() { const xhr = new XMLHttpRequest(); xhr.onprogress = function() { // 截取本次新增的响应文本片段 const newResponse = xhr.responseText.substring(lastProcessPos); // 按换行拆分每段JSON,过滤空内容 const jsonChunks = newResponse.split('\n').filter(chunk => chunk.trim() !== ''); let processedLen = 0; // 逐段解析JSON并渲染 jsonChunks.forEach(chunk => { try { const data = JSON.parse(chunk); const txt = 'Min: ' + data.num1 + ' | Max: ' + data.num2 + ' | Numeration: ' + data.id; $('p').prepend(txt + '<br>'); processedLen += chunk.length + 1; // +1 是分隔的换行符长度 } catch (err) { // 遇到半截JSON(TCP分包导致)时停止处理,留到下次进度事件拼接完整后再解析 return false; } }); // 更新已处理位置 lastProcessPos += processedLen; }; return xhr; }, beforeSend: function() { $('.loader').show(); }, complete: function() { // 所有内容传输完成后隐藏加载态 $('.loader').hide(); } }); }); });
逻辑说明
- 前端
onprogress事件会在服务端每推送一段数据到客户端时触发,无需等待请求完全结束 - 用换行符作为每段JSON的分隔标记,配合已处理位置标记,不会重复解析已经渲染过的内容
- 增加了JSON解析容错逻辑,如果遇到TCP分包切出的半截JSON,会留到下一次进度事件和后续内容拼接完整后再解析,避免脚本报错
内容的提问来源于stack exchange,提问作者frozenade
相关产品推荐
相关产品推荐

