一.fetch与axios的比较
fetch
和
axios
是两种常用的 HTTP 请求工具,在前端开发中经常使用来与后端进行通信。它们有一些重要的区别和各自的优缺点。其中流式处理在我们实现逐字输出中起到了主要作用
fetch
fetch
是现代浏览器内置的 API,用于发起网络请求。它返回的是一个
Promise
,更加现代和简洁。
优点:
- 内置于浏览器:不需要额外的库文件,减少了依赖。
- 基于 Promise:更简洁和易于链式调用。
- 流式处理:支持
ReadableStream,方便处理大数据量或流式数据。 - 灵活性高:可以使用
Request和Response对象进行更多自定义操作。
缺点:
- 不自动处理 JSON:需要手动调用
response.json()来处理 JSON 数据。 - 不支持取消请求:没有内置的取消请求机制(虽然可以使用
AbortController进行额外处理)。 - 缺少一些常用功能:如超时设置、全局配置、拦截器等。
二. fetch的使用
使用步骤
- 发送请求:
const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ question: question })});- 使用fetch发送 POST 请求到指定的 URL,设置请求头Content-Type为application/json,并将question作为请求体发送。 - 检查响应体是否支持流:
if (!response.body) { throw new Error('ReadableStream not yet supported in this browser.');}- 检查response.body是否存在。如果不存在,抛出错误,表明浏览器不支持ReadableStream。 - 读取数据流:
jconst reader = response.body.getReader();let decoder = new TextDecoder('utf-8');- 获取读取器response.body.getReader()用于读取响应的流数据。- 创建一个TextDecoder实例,用于将二进制数据解码为字符串。 - 处理数据流:
while (true) { const { done, value } = await reader.read(); if (done) break; // 如果数据流结束,退出循环 const chunkData = decoder.decode(value, { stream: true }).trim(); this.streams.push(chunkData); this.updateMessagesFromStreams();}- 使用while (true)循环持续读取数据流。-reader.read()返回一个 Promise,解析后得到一个对象{ done, value }:-done:如果为true,表示数据流已结束,退出循环。-value:当前读取的二进制数据块。- 使用decoder.decode将二进制数据解码为字符串,并存入streams数组。- 每读取到一个数据块后,调用updateMessagesFromStreams方法来更新messages数组。 - 更新 messages 数组:
updateMessagesFromStreams() { const combinedMessage = this.streams.join(''); this.messages = [{ content: combinedMessage, type: 'bot' }];}-updateMessagesFromStreams方法将streams数组中的所有字符串连接成一个完整的字符串。- 更新messages数组,将连接后的字符串作为新的消息内容。
总结
通过使用
fetch
进行流式请求,可以有效地逐块处理数据并实时更新前端显示。与
axios
类似,
fetch
的
ReadableStream
使得处理流数据更加简洁和现代化。此方法在数据量较大或需要逐步处理和显示数据的场景下尤为适用。
三. 实际代码实现
async sendToBackend(question) {
const url = "http://localhost:5000/api/query"; // 确保这是正确的后端URL
this.streams = [];
this.sending = true;
try {
// 发送流式请求
const response = await axios({
method: 'post',
url: url,
data: { question: question },
responseType: 'stream'
});
const reader = response.data.getReader();
let decoder = new TextDecoder('utf-8');
// 读取数据流
while (true) {
const { done, value } = await reader.read();
if (done) break; // 如果数据流结束,退出循环
// 假设数据是字符串形式的,直接存入 streams 数组
const chunkData = decoder.decode(value, { stream: true }).trim(); // 假设数据是一个个汉字字符串
this.streams.push(chunkData);
// 更新 messages 数组(每获取到一个块就更新一次)
this.updateMessagesFromStreams();
}
// 标记发送完成
this.sending = false;
} catch (error) {
console.error('Error sending to backend:', error);
this.sending = false;
}
},
updateMessagesFromStreams() {
// 将 streams 数组中的所有字符串连接成一个,并存入 messages 数组
const combinedMessage = this.streams.join('');
this.messages = [{ content: combinedMessage, type: 'bot' }];
}
版权归原作者 WUJI02 所有, 如有侵权,请联系我们删除。