<span id="response_row" class="result-streaming">{{ item.assistantContent }}span>
.result-streaming:after {
-webkit-animation: blink 1s steps(5, start) infinite;
animation: blink 1s steps(5, start) infinite;
content: "▋";
margin-left: 0.25rem;
vertical-align: baseline;
}
先贴最后成功使用的
使用fetchEventSource方法
参考代码:https://blog.csdn.net/cuiyuchen111/article/details/129468291
参考/下载文档:https://www.npmjs.com/package/@microsoft/fetch-event-source?activeTab=readme
<p v-if="item.requestFlag" class="content robot_content"><span id="response_row" class="result-streaming">{{ item.assistantContent }}span>p>
<p class="content robot_content"><span v-html="item.assistantContent">span>p>
async getResponseFromAPI() {
const that = this;
this.sendLoading = true;
// 用户提问时间
let userTime = that.getNowTime();
const form = JSON.parse(JSON.stringify(this.form));
console.log(form, "请求里的form");
//物理添加 页面
that.conversations.push({
userContentId: "",
userContent: form.prompt,
userContentDatetime: userTime,
assistantContentId: "",
assistantContent: "",
assistantContentDatetime: userTime,
requestFlag: true,
});
// 对话请求
const abortController = new AbortController();
let formData = new FormData();
formData.append("chatid", this.currentChatId);
formData.append("clientid", form.clientid);
formData.append("prompt", form.prompt);
const url = "xxxxx";
const headers = new Headers();
const body = formData;
const eventSource = fetchEventSource(url, {
method: "POST",
headers,
body,
signal: abortController.signal,
onmessage(e) {
that.handleScrollBottom();
that.form.prompt = "";
const response_row = document.getElementById("response_row");
console.log(e.data);
let res = JSON.parse(e.data);
let index = that.conversations.length - 1;
if (res.message == "[DONE]") {
res.data = JSON.parse(res.data);
console.log(res.data);
let obj = {
userContentId: res.data.userContentId,
userContent: res.data.userContent,
userContentDatetime: userTime,
assistantContentId: res.data.assistantContentId,
assistantContent: res.data.assistantContent,
assistantContentDatetime: that.getNowTime(),
requestFlag: false,
};
console.log(obj);
that.$set(that.conversations, index, obj);
that.sendLoading = false;
abortController.abort();
eventSource.close();
console.log("我是结束!!");
} else {
var content = res.data;
response_row.innerText += content;
// console.log(content)
// if (content.includes("[ENTRY]")) {
// content = content.replaceAll("[ENTRY]", "\n");
// }
}
},
onclose() {
console.log("close");
that.sendLoading = false;
abortController.abort();
eventSource.close();
},
onerror(error) {
let index = that.conversations.length - 1;
that.conversations.splice(index, 1);
that.sendLoading = false;
console.log("error", error);
abortController.abort();
eventSource.close();
},
});
}
遇到的问题:
1.只调用一次事件 但fetch请求发送了两次或多次且终止失败
//按照fetchEventSource文档内的写法 请求暂停无效
const ctrl = new AbortController();
fetchEventSource('/api/sse', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
foo: 'bar'
}),
signal: ctrl.signal,
});
//而后看到一种说法,fetchEventSource是针对EventSource API的,而不是xhr或fetch API
//因此定义EventSource存储接口所返回的数据 使用EventSource的暂停方法 =》 fetchEventSource暂停成功
const eventSource = fetchEventSource(url, {
method: "POST",
headers,
body,
signal: abortController.signal,
onmessage(e) {
eventSource.close();
},
onclose() {
eventSource.close();
},
onerror(error) {
eventSource.close();
},
});
1.使用fetch方式进行sse流式处理
优点:可以使用post请求
缺点:获取到的数据处理困难 获取事件返回格式或有错误
参考代码:https://blog.csdn.net/betterAndBetter_/article/details/129900233
http://681314.com/A/YaHyYpjoPF
async function send() {
const input = document.getElementById("input").value;
const output = document.getElementById("output");
output.innerText = "";
const url = "/api/stream";
const data = { "Prompt": input };
//直接获取 Fetch 的response, 无法使用 await的话, Promise的方式也是可以的。
const response = await fetch(url, {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json"
}
})
//获取UTF8的解码
const encode = new TextDecoder("utf-8");
//获取body的reader
const reader = response.body.getReader();
// 循环读取reponse中的内容
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
// 解码内容
const text = encode.decode(value);
// 当获取错误token时,输出错误信息
if (text === "" ) {
output.innerText = "Error";
break;
} else {
// 获取正常信息时,逐字追加输出
output.innerText += text;
}
}
}
【记得补截图】
2.使用eventSource进行sse流式处理
优点:获取到的数据格式规范 易处理
缺点:无法使用post请求
参考b站视频:https://www.bilibili.com/video/BV1QA411C7mN/?spm_id_from=333.880.my_history.page.click&vd_source=384646ea9baa6985ceb5331bff5b87b0
var rsource = (this.rsource = new EventSource(
`/api/chat/repeat/${this.cid}`
));
rsource.addEventListener("open", function () {
console.log("connect");
});
//如果服务器响应报文中没有指明事件,默认触发message事件
rsource.addEventListener("message", function (e) {
console.log(`resp:(${e.data})`);
var rconv = that.conversation[that.conversation.length - 1];
if (e.data == "[DONE]") {
rsource.close();
rconv["loading"] = false;
that.convLoading = false;
that.refrechConversation();
that.rsource = undefined;
return;
}
var content = e.data;
if (content.includes("[ENTRY]")) {
content = content.replaceAll("[ENTRY]", "\n");
}
// 滚动到最下面
that.handleScrollBottom();
var idx = rconv.idx;
rconv["speeches"][idx] += content;
that.refrechConversation();
});
//发生错误,则会触发error事件
rsource.addEventListener("error", function (e) {
console.log("error:" + e.data);
rsource.close();
that.rsource = undefined;
});
由于eventSource获取到的数据比fetch流畅许多,所以研究过eventSource能否使用post请求,使用过以下代码,但失败了
3.fetch和eventSource同时使用
优点:可以很顺利的请求并且获取到数据
缺点:fetch支持post eventSource不支持post 对接口请求方式有要求 几乎不太能兼容
// 获取表单元素
const form = document.querySelector('#my-form');
// 监听表单提交事件
form.addEventListener('submit', (event) => {
event.preventDefault(); // 阻止默认提交行为
const formData = new FormData(form); // 创建 FormData 对象
// 发送 POST 请求并接收 SSE 流式输出
fetch('/api/submit-form', {
method: 'POST',
body: formData
}).then((response) => {
// 如果请求成功,则创建 EventSource 对象监听 SSE 输出
if (response.ok) {
const eventSource = new EventSource('/api/stream');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data); // 处理接收到的数据
};
eventSource.onerror = (error) => { // 监听错误事件
console.error(error);
};
}
}).catch((error) => {
console.error(error);
});
});