举个例子,在写llm的chat的时候,经常会出现需要取消请求的场景。 如何在前端取消请求,涉及到一个接口:AbortController.AbortController() - Web API 接口参考 | MDN 在原生的js的写法,参考mdn的写法。 let controller; const 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 ( ... ) }