从零开发 AI Chatbot:Loading State 加载状态管理
一、前言
在之前的开发中,我们已经学习了消息渲染、Markdown 渲染、代码高亮、自动滚动等前端知识。
随着 KK Chat 的功能不断完善,聊天界面的交互体验也变得越来越重要。
在使用 AI Chatbot 时,我们经常会看到这样的过程:
这里涉及一个重要的前端概念:Loading State(加载状态)。
如果没有合理的加载状态管理,用户发送消息后可能不知道请求是否成功,也可能连续点击发送按钮,导致重复请求。
因此,今天我们来学习 Loading State 的基本原理,以及如何将它应用到 KK Chat 中。
二、什么是 Loading State?
Loading State 是指程序在执行异步操作期间,用来表示当前处理状态的一种机制。
在 AI Chatbot 中,一次请求通常需要经过以下流程:
用户发送消息
|
v
开始请求 AI
|
v
显示加载状态
|
v
接收 AI 回复
|
v
渲染回复内容
|
v
结束加载状态
加载状态不仅可以用来显示动画,还可以控制发送按钮、输入框等交互元素。
例如:
- 请求进行中:显示“正在生成回复”。
- 请求成功:显示 AI 的回复。
- 请求失败:显示错误提示。
- 请求结束:允许用户继续发送消息。
因此,Loading State 不只是一个视觉效果,更是前端管理异步请求的重要手段。
三、使用 isLoading 管理状态
在 JavaScript 中,我们可以使用一个布尔变量表示请求是否正在进行。
let isLoading = false;
其中:
- false:当前没有正在执行的请求。
- true:当前正在处理请求。
当用户发送消息时,将状态设置为 true;请求结束后,再恢复为 false。
例如:
async function sendMessage() {
isLoading = true;
try {
// 在这里执行异步请求
} finally {
isLoading = false;
}
}
这里使用了 try…finally。
try 用于执行主要操作,finally 中的代码则会在流程离开 try 后执行,包括发生异常的情况。
这样可以避免请求失败后,加载状态一直停留在进行中。
需要注意:上面只是状态管理的基础示例。实际项目还需要根据 isLoading 的变化更新页面。
四、如何将加载状态显示在页面上?
假设聊天界面有一个发送按钮和一个状态提示区域:
<button id="send-btn">发送</button>
<div id="loading-status"></div>
我们可以通过 JavaScript 更新它们的状态:
const sendBtn = document.getElementById("send-btn");
const loadingStatus = document.getElementById("loading-status");
function setLoading(loading) {
sendBtn.disabled = loading;
loadingStatus.textContent = loading
? "AI 正在生成回复…"
: "";
}
当调用:
setLoading(true);
页面会显示加载提示,并禁用发送按钮。
当调用:
setLoading(false);
加载提示消失,发送按钮恢复可用。
这种方式可以将状态管理集中在一个函数中,避免在多个地方重复编写相同的界面更新逻辑。
五、在异步请求中使用 Loading State
接下来,将加载状态与异步请求结合起来。
下面是一个简化的示例:
async function sendMessage() {
if (isLoading) {
return;
}
isLoading = true;
setLoading(true);
try {
const response = await fetch("/api/chat", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
message: "你好"
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error("请求失败:", error);
loadingStatus.textContent = "请求失败,请稍后重试。";
} finally {
isLoading = false;
sendBtn.disabled = false;
}
}
这个示例展示了完整的异步请求流程:
需要注意,示例中的 /api/chat、请求参数和响应格式需要根据实际后端接口进行调整。
此外,HTTP 请求返回 4xx 或 5xx 状态码时,fetch() 通常不会自动抛出异常,因此需要通过 response.ok 主动检查。
六、Loading State 与 Streaming 的关系
在 KK Chat 中,我们还需要考虑 Streaming(流式输出)。
普通请求与 Streaming 的处理流程有所不同。
1. 普通请求
发送消息
|
v
等待响应
|
v
获取完整回复
|
v
显示回复
|
v
结束加载
2. Streaming 请求
发送消息
|
v
开始接收数据
|
v
持续读取数据片段
|
v
逐步更新 AI 回复
|
v
数据流结束
|
v
结束加载
对于 Streaming,不能在收到第一个数据片段时就认为整个请求已经结束。
正确的做法是:
在数据流正常结束,或者请求发生错误并完成必要的清理后,再结束本次加载状态。
这样才能保证加载提示、发送按钮和 AI 回复的显示状态保持一致。
七、实践中可能遇到的问题
1. 请求失败后,发送按钮一直处于禁用状态
原因:
程序只在请求成功时恢复按钮,没有处理异常情况。
解决方案:
使用 finally 统一恢复状态,避免成功和失败的分支各自维护一套清理逻辑。
2. 用户连续点击发送,产生重复请求
原因:
请求进行期间,程序仍然允许用户重复提交。
解决方案:
发送请求前检查 isLoading,并在请求开始时禁用发送按钮。
需要注意,前端限制主要用于改善交互体验,并不能代替后端必要的请求校验。
3. Streaming 已经开始输出,但加载状态提前结束
原因:
程序将“收到第一段数据”误认为“整个请求已经结束”。
解决方案:
将加载状态的生命周期与完整的数据流处理过程绑定,在读取完成或发生异常后再进行清理。
4. 加载动画一直显示
原因:
某些代码路径没有恢复状态,或者数据流异常后没有执行清理逻辑。
解决方案:
梳理请求的完整生命周期,检查成功、失败和中断等路径是否都能正确结束加载状态。
八、今天的收获
通过今天的学习,我进一步理解了前端状态管理与异步请求之间的关系。
主要知识点包括:
- Loading State 的概念与作用。
- 使用布尔变量表示请求状态。
- 使用 try…catch…finally 处理异步操作。
- 使用 disabled 控制发送按钮。
- 通过 response.ok 检查 HTTP 响应状态。
- 理解 Streaming 场景下加载状态的生命周期。
其中最重要的一点是:
加载状态必须与实际请求的生命周期保持一致。
它不是简单地显示一个动画,而是决定界面在请求开始、处理中、成功和失败时应该如何响应。
对于 KK Chat 来说,合理的加载状态管理能够减少重复提交,改善用户反馈,也为后续实现停止生成、请求取消等功能打下基础。
KJ
项目:KK Chat
系列:《从零开发 AI Chatbot》


