使用场景
- 实时输出场景(GPT)
- 数据实时要求比较高(商场首页)
使用原理
- 流式(chunked)接口是一种处理大量数据的有效方式,特别是在需要处理大文件或长时间运行的任务时。这种接口允许数据分块逐步传输,而不是一次性将整个数据传输完成。这对于内存管理和网络传输来说都是非常有益的。
- 使用fetch请求接口在循环等待接收数据,实现chunked接收。
H5用法
1、简单实现的例子: 实现application/json请求
const response = await fetch('/api/chunked', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
keyword: 'test-kw'
})
});
if(response.body){
const reader = response.body.getReader();
while (true) {
const { value, done } = await reader!.read();
const msg = new TextDecoder().decode(value);
console.log("接收的数据是=>" + msg);
if (done) {
break;
}
}
}
2、实际的一个post请求例子(包括:粘包处理)
// 定义一个睡眠函数
const sleep = async function (time = 0) {
await setTimeout(() => {}, time);
};
// 定义一个请求方法
const request = function (data: object, onChunkedReceived: (data) => void) {
return new Promise((resolve, reject) => {
(async () => {
// 记录接收的时间
const startTime = new Date().getTime();
const getUseTime = () => new Date().getTime() - startTime;
// 开始请求
const response = await fetch('/api/chunked', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (response.body) {
// 获取响应的内容reader
const reader = response.body.getReader();
let responseStr = '';
const stickyTag = '}{';
const parseResponse = function () {
// 粘包处理(后端定义为json字符串,那么粘包标记为:“}{”)之切割
const chunkedList = responseStr.split(stickyTag);
if (chunkedList.length > 0) {
// 粘包处理,补齐分包
const newChunkeds = chunkedList.map((it, i) => {
let str = it;
if (i > 0) str = '{' + str;
if (i < chunkedList.length) str += '}';
return str;
});
// 分别对分包处理并且回调
newChunkeds.forEach((chunked, i) => {
try {
// 后端定义:每个分包为json字符串
const json = JSON.parse(chunked);
onChunkedReceived(json);
} catch (err) {
if (i === newChunkeds.length - 1) {
// 最后一个chunked没有解析,可能是chunked不完整,需要记录到
responseStr = chunked;
} else {
// 如果非最后一个chunked没有解析,那么内容有问题,那么就丢弃
console.error('chunked解析错误,已经丢弃=>', chunked);
}
}
});
} else {
console.error('响应分割失败', { responseStr, chunkedList });
}
};
// 建立循环,等待内容读取完毕
while (true) {
const { value, done } = await reader!.read();
const msg = new TextDecoder().decode(value);
console.log('粘包的个数=>', msg.split(stickyTag).length - 1, '接收的时间=>' + getUseTime());
responseStr += msg;
parseResponse();
await sleep(1);
if (done) {
break;
}
}
resolve({ msg: '接收数据完毕' });
} else {
reject({ msg: '发起请求错误', data: response });
}
})();
});
};
特别注意
- 粘包方面: 接口在发送chunked速度过快时候,可能会导致前端接收的数据粘在一起,那么前端就得进行粘包处理。网络速度慢也会导致粘包,综合来说:前端需要对接收的chunked进行粘包处理(不管接收的什么结果)
- 每次等待读取响应结果间隙建议睡眠一段时间再继续获取。如果连续获取,可能会导致chunked处理结果问题。
- axios不支持chunked形式,如果一定需要用,需要用到axios对应的chunked插件。