在前端使用abort取消请求
Updated:
在前端使用 abort 取消请求
举个例子,在写 llm 的 chat 的时候,经常会出现需要取消请求的场景。
如何在前端取消请求,涉及到一个接口:AbortController.AbortController() - Web API 接口参考 | MDN
在原生的 js 的写法,参考 mdn 的写法。
let controllerconst url = "video.mp4"
const downloadBtn = document.querySelector(".download")const abortBtn = document.querySelector(".abort")
downloadBtn.addEventListener("click", fetchVideo)
abortBtn.addEventListener("click", () => { if (controller) { controller.abort() controller = null console.log("Download aborted") }})
function fetchVideo() { controller = new AbortController() const signal = controller.signal fetch(url, { signal }) .then((response) => { console.log("Download complete", response) }) .catch((err) => { console.error(`Download error: ${err.message}`) })}
在 react 的写法
import React, { useState, useEffect } from "react"
const RequestComponent = () => { const [responseData, setResponseData] = useState(null) const [error, setError] = useState(null) const [loading, setLoading] = useState(false) const [controller, setController] = useState(null)
useEffect(() => { // 组件被卸载的时候,取消请求 return () => { if (controller) { controller.abort() } } }, [controller])
const fetchData = async () => { setLoading(true) setError(null)
const abortController = new AbortController() setController(abortController)
try { const response = await fetch("https://api.example.com/data", { signal: abortController.signal, })
if (!response.ok) { throw new Error("Network response was not ok") }
const data = await response.json() setResponseData(data) } catch (error) { if (error.name === "AbortError") { console.log("Request canceled by user") } else { setError(error) } } finally { setLoading(false) } }
const cancelRequest = () => { if (controller) { controller.abort() } }
return ( <div> <button onClick={fetchData} disabled={loading}> {loading ? "Loading..." : "Fetch Data"} </button> <button onClick={cancelRequest} disabled={!loading}> Cancel Request </button> {error && <div>Error: {error.message}</div>} {responseData && <div>Data: {JSON.stringify(responseData)}</div>} </div> )}
export default RequestComponent
在 solidjs 中的写法,可以参考 diu 老师的 GitHub - anse-app/chatgpt-demo: Minimal web UI for ChatGPT.
import { Index, Show, createEffect, createSignal, onCleanup, onMount } from 'solid-js'import { useThrottleFn } from 'solidjs-use'import { generateSignature } from '@/utils/auth'import IconClear from './icons/Clear'import MessageItem from './MessageItem'import SystemRoleSettings from './SystemRoleSettings'import ErrorMessageItem from './ErrorMessageItem'import type { ChatMessage, ErrorMessage } from '@/types'
export default () => { const [controller, setController] = createSignal<AbortController>(null)
const requestWithLatestMessage = async() => { setLoading(true) setCurrentAssistantMessage('') setCurrentError(null) const storagePassword = localStorage.getItem('pass') try { const controller = new AbortController() setController(controller) const requestMessageList = messageList().slice(-maxHistoryMessages) if (currentSystemRoleSettings()) { requestMessageList.unshift({ role: 'system', content: currentSystemRoleSettings(), }) } const timestamp = Date.now() const response = await fetch('/api/generate', { method: 'POST', body: JSON.stringify({ messages: requestMessageList, time: timestamp, pass: storagePassword, sign: await generateSignature({ t: timestamp, m: requestMessageList?.[requestMessageList.length - 1]?.content || '', }), temperature: temperature(), }), signal: controller.signal, }) if (!response.ok) { const error = await response.json() console.error(error.error) setCurrentError(error.error) throw new Error('Request failed') } const data = response.body if (!data) throw new Error('No data')
const reader = data.getReader() const decoder = new TextDecoder('utf-8') let done = false
while (!done) { const { value, done: readerDone } = await reader.read() if (value) { const char = decoder.decode(value) if (char === '\n' && currentAssistantMessage().endsWith('\n')) continue
if (char) setCurrentAssistantMessage(currentAssistantMessage() + char)
isStick() && instantToBottom() } done = readerDone } } catch (e) { console.error(e) setLoading(false) setController(null) return } archiveCurrentMessage() isStick() && instantToBottom() }
const stopStreamFetch = () => { if (controller()) { controller().abort() ... } }
return ( ... )}
感谢阅读到这里,期待收到更多的反馈
欢迎关注公众号
kazoottt
