如何取消 fetch 的流式请求并处理错误信息
引言:
最近在做的ai项目,产品提了一个需求,要求如果ai正在输出内容,用户再次提交了prompt,终止当前的请求,去发送新的请求
代码示例
封装请求
export const getAiMessage = ({ message, session_id, callback, signal }) => {
fetch(`${baseURL}/api/question/ask`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: sessionStorage.getItem("token") || "",
},
body: JSON.stringify({ message, session_id: session_id || "" }),
signal: signal,
})
.then(async (response) => {
// 请求成功
if (response.ok) {
const reader = response.body.getReader();
const decoder = new TextDecoder("utf-8");
while (true) {
try {
const { value, done } = await reader.read();
if (done) {
break;
}
const chunkValue = decoder.decode(value).split("\n\n");
for (const item of chunkValue) {
if (item.startsWith("data:")) {
const itemData = JSON.parse(item.replace("data:", " ").trim());
callback(itemData);
}
}
} catch (e) {
if (e.name === "AbortError") {
console.log("请求已取消", e);
return;
}
console.log("数据流读取出错", e);
}
}
console.log("响应结束");
}
})
.catch((e) => {
if (e.name === "AbortError") {
console.log("请求已取消", e);
} else {
console.log("请求出错", e);
}
});
};
页面使用
handleSend(e) {
if (e) {
e.preventDefault();
}
if (!this.userPrompt || this.isMessageLoading) {
return;
}
if (this.streamController) {
this.streamController.abort();
}
this.isMessageLoading = true;
this.aiMessageList.push({
role: "user",
content: this.userPrompt,
});
this.changeMessageScroll();
const aiMessageIndex = this.aiMessageList.length;
this.aiMessageList[aiMessageIndex] = {
role: "assistant",
content: "",
};
this.streamController = new AbortController();
const obj = {
message: this.userPrompt,
session_id: this.session_id,
callback: (res) => {
this.isMessageLoading = false;
this.result += res.text;
this.session_id = res.session_id;
const converter = new showdown.Converter();
this.aiMessageList[aiMessageIndex] = {
role: "assistant",
content: converter.makeHtml(this.result),
};
this.changeMessageScroll();
},
signal: this.streamController.signal,
};
this.userPrompt = "";
this.result = "";
getAiMessage(obj);
},
代码详解
我们主要通过创建AbortController对象,将AbortController中的signal传递给fetch,然后通过调用AbortController的abort方法去取消请求,主要代码如下
如果想取消请求,在想取消请求的代码逻辑处 调用abort方法就好
错误处理
成功取消请求后会被 catch 捕获。注意 ! ! ! !,如果使用的是流式请求,一定要在每次处理接收到的流数据时进行错误处理,并在捕获到错误时使用 return 退出当前方法。否则,请求会继续进行,并且错误信息会不断打印,导致页面卡死。因为我们取消请求,也只是抛出了一个请求取消的报错信息,具体的代码逻辑需要我们自己处理。