电商网站运营怎么做,网站建设 系统 排名,网站建设初衷,网站建设调查内容有哪些上一篇文章中写到了使用Server-Sent Events (SSE)#xff0c;并获取message里面的内容。 本篇文章主要是写#xff0c;具体该如何实现的具体代码#xff0c;代码见下方#xff0c;可直接拿 async submitConsult() {this.scrollToBottom();if (!this.$checkLogin()) return;…上一篇文章中写到了使用Server-Sent Events (SSE)并获取message里面的内容。 本篇文章主要是写具体该如何实现的具体代码代码见下方可直接拿 async submitConsult() {this.scrollToBottom();if (!this.$checkLogin()) return;let _consultInfo JSON.parse(JSON.stringify(this.consultInfo));if (this.btnLoading || !_consultInfo.user_input) return;this.btnLoading true;let myInfo {action_id: my,question: _consultInfo.user_input.replace(/\\n/g, \n),};this.currentQuestion _consultInfo.user_input;this.historyList.push(myInfo);this.addPopoverEventListeners();this.consultInfo.user_input ;_consultInfo.select_param this.label;const url BASEURL xxxx;try {const controller new AbortController();const payload {method: POST,body: JSON.stringify(_consultInfo),signal: controller.signal,headers: {Content-Type: application/json,},};const requestTimeoutId setTimeout(() controller.abort(), 50000);let responseText ;let remainText ;let finished false;const animateResponseText async () {if (finished || controller.signal.aborted) {responseText remainText;return;}if (remainText.length 0) {const fetchCount Math.max(1, Math.round(remainText.length / 5000));const fetchText remainText.slice(0, fetchCount);responseText fetchText;remainText remainText.slice(fetchCount);if (responseText.trim()) {this.readWriter(responseText);}}requestAnimationFrame(animateResponseText);};const finish () {if (finished) {finished true;if (remainText.trim()) {this.readWriter(remainText, true);}}};let that this;controller.signal.onabort finish;let lashIndex 0;await fetchEventSource(url, {...payload,async onopen(res) {clearTimeout(requestTimeoutId);const contentType res.headers.get(content-type);if (contentType contentType.startsWith(text/plain)) {responseText await res.clone().text();return finish();}if (!res.ok || res.status ! 200 || !res.headers.get(content-type).startsWith(EventStreamContentType)) {try { const errorText await res.clone().text();} catch (e) {console.log(异常信息:, e);}return finish();}},onmessage(msg) {that.isReadText true; // 开始发送请求相当于正在进行打印const text msg.data;if (text) {remainText textthat.readWriter(text);}},onclose() {finished truefinish();},onerror(e) {console.log(e);},openWhenHidden: true,});} catch (e) {console.log(e, -------e);} finally {this.hasNewAction false;}},async submitConsult() { … }: 定义了一个异步方法 submitConsult 用于提交用户的咨询。this.scrollToBottom();: 调用方法滚动到消息容器的底部。if (!this.$checkLogin()) return;: 如果用户未登录则返回。let _consultInfo JSON.parse(JSON.stringify(this.consultInfo));: 创建 consultInfo 的深拷贝以避免直接修改原始数据。if (this.btnLoading || !_consultInfo.user_input) return;: 如果按钮处于加载状态或用户输入为空则返回。this.btnLoading true;: 设置按钮加载状态为真。let myInfo { … };: 创建一个包含用户问题的对象。this.currentQuestion _consultInfo.user_input;: 设置当前问题为用户输入。this.historyList.push(myInfo);: 将用户问题添加到历史列表。this.addPopoverEventListeners();: 添加弹出框事件监听器。this.consultInfo.user_input “”;: 清空用户输入。_consultInfo.select_param this.label;: 设置选择参数为当前标签。
async submitConsult() {this.scrollToBottom(); // 确保在发送新消息时滚动到消息列表的底部if (!this.$checkLogin()) return; // 检查用户是否登录如果未登录则返回不执行后续操作let _consultInfo JSON.parse(JSON.stringify(this.consultInfo)); // 创建consultInfo对象的深拷贝以避免直接修改原始数据if (this.btnLoading || !_consultInfo.user_input) return; // 如果按钮处于加载状态或用户输入为空则不执行后续操作this.btnLoading true; // 设置按钮的加载状态为true表示正在处理中let myInfo {action_id: my,question: _consultInfo.user_input.replace(/\\n/g, \n), // 将用户输入的字符串中的转义换行符替换为实际的换行符};this.currentQuestion _consultInfo.user_input; // 将用户输入的问题保存到currentQuestion属性中this.historyList.push(myInfo); // 将用户的问题添加到历史消息列表中this.addPopoverEventListeners(); // 添加弹出框事件监听器this.consultInfo.user_input ; // 清空用户输入框_consultInfo.select_param this.label; // 将当前选中的标签赋值给consultInfo对象的select_param属性const url BASEURL chat/v2; // 定义请求的URLBASEURL是一个常量表示API的基础路径// ...省略了部分代码...
}try {const controller new AbortController(); // 创建一个控制器用于取消fetch请求const payload {method: POST, // 设置请求方法为POSTbody: JSON.stringify(_consultInfo), // 将用户咨询信息转换为JSON字符串作为请求体signal: controller.signal, // 将控制器的signal属性传递给fetch用于取消请求headers: {Content-Type: application/json, // 设置请求头表明请求体是JSON格式},};const requestTimeoutId setTimeout(() controller.abort(), 50000); // 设置一个50秒的超时定时器如果请求超时则取消请求let responseText ; // 初始化一个变量来存储响应文本let remainText ; // 初始化一个变量来存储剩余的文本let finished false; // 初始化一个标志位表示是否完成const animateResponseText async () {// ...省略了部分代码...};const finish () {// ...省略了部分代码...};let that this; // 由于在事件回调中this的指向可能会变化这里用that来保存当前的thiscontroller.signal.onabort finish; // 当请求被取消时执行finish函数let lashIndex 0; // 初始化一个变量用于记录上一次的索引位置await fetchEventSource(url, {// ...省略了部分代码...});} catch (e) {console.log(e, -------e); // 如果请求过程中发生异常打印异常信息} finally {this.hasNewAction false; // 无论请求成功还是失败将hasNewAction设置为false}
}fetchEventSource是一个用于处理服务器发送事件Server-Sent Events, SSE的函数它能够处理来自服务器的流式响应。这个方法中的逻辑主要是发送用户的问题到服务器并处理返回的答案将其显示在界面上。
animateResponseText 函数
animateResponseText函数的作用是动态地将服务器响应的文本内容逐渐显示到用户界面上增强用户体验。这个函数可能会定期检查是否有新的文本内容到来如果有就将新内容添加到界面上。这里是一个假设的实现
const animateResponseText async () {if (finished || controller.signal.aborted) {responseText remainText;return;}if (remainText.length 0) {const fetchCount Math.max(1, Math.round(remainText.length / 50));const fetchText remainText.slice(0, fetchCount);responseText fetchText;remainText remainText.slice(fetchCount);if (responseText.trim()) {this.readWriter(responseText);}}requestAnimationFrame(animateResponseText);
};检查finished或controller.signal.aborted如果请求完成或被中止则将剩余文本remainText追加到responseText并结束函数执行。如果remainText包含未处理的文本计算需要获取的文本长度fetchCount并从remainText中获取这段文本追加到responseText。this.readWriter(responseText);调用readWriter方法可能用于将文本显示到界面上。使用requestAnimationFrame(animateResponseText);递归调用自身以动态更新内容。
fetchEventSource 函数
fetchEventSource是处理服务器发送事件Server-Sent Events, SSE的API用于接收服务器端的实时数据流。这个函数通常用于订阅服务器端的消息然后将这些消息动态地展示给用户。一个简化的实现示例如下
await fetchEventSource(url, {...payload,async onopen(res) {clearTimeout(requestTimeoutId);if (!res.ok || !res.headers.get(content-type).startsWith(EventStreamContentType)) {finish();}},onmessage(msg) {that.isReadText true;const text msg.data;if (text) {remainText text;that.readWriter(text);}},onclose() {finished true;finish();},onerror(e) {console.log(e);},openWhenHidden: true,
});onopen(res): 当连接成功打开时调用。取消之前设置的超时定时器。检查响应状态如果不是200或内容类型不匹配则调用finish函数处理结束逻辑。onmessage(msg): 当从服务器接收到消息时调用。将接收到的数据赋值给remainText然后调用readWriter方法处理文本显示。onclose(): 当连接关闭时调用。设置finished true并调用finish函数处理结束逻辑。onerror(e): 当发生错误时调用打印错误信息。
这样的处理流程能够实现与服务器端的实时通信适用于聊天应用、实时通知显示等场景。
使文本的显示看起来更加平滑和自然
const fetchCount Math.max(1, Math.round(remainText.length / 50));
const fetchText remainText.slice(0, fetchCount);
responseText fetchText;
remainText remainText.slice(fetchCount);这这段代码是处理从服务器接收到的文本数据并将其逐步显示到用户界面上。下面是对这几行代码的详细解释
const fetchCount Math.max(1, Math.round(remainText.length / 50));remainText.length / 50: 这里将剩余文本remainText的长度除以50目的是为了分批处理文本每批大约处理文本长度的1/50。Math.round(...): 四舍五入计算出的结果确保fetchCount是一个整数。Math.max(1, ...): 确保即使计算结果小于1fetchCount的值至少也是1。这意味着无论如何每次至少会处理一个字符。
const fetchText remainText.slice(0, fetchCount);remainText.slice(0, fetchCount): 从remainText中提取从索引0开始到fetchCount索引结束的子字符串。这部分文本是将要追加到responseText的内容并且是下一次动画帧要显示的文本。
responseText fetchText;responseText fetchText: 将提取出来的文本fetchText追加到responseText变量中。responseText变量用于累积已经处理并准备显示给用户的文本。
remainText remainText.slice(fetchCount);remainText.slice(fetchCount): 更新remainText变量移除已经追加到responseText的部分。remainText现在只包含尚未处理的文本这部分文本将在下一次动画帧中被处理。
整体来看这段代码的作用是将从服务器接收到的文本分批次动态地显示到界面上而不是一次性显示全部文本从而提高用户体验使文本的显示看起来更加平滑和自然。