AbortController 取消请求
停止生成、超时控制
5.1 基础用法
javascript
const controller = new AbortController();
// 传入 signal
const response = await fetch('/api/chat', {
method: 'POST',
signal: controller.signal,
body: JSON.stringify({ prompt: 'Hello' })
});
// 用户点击 "停止生成"
document.getElementById('stop').onclick = () => {
controller.abort();
};5.2 完整 ChatStream 类
javascript
class ChatStream {
constructor() {
this.controller = null;
}
async stream(messages, onToken) {
// 取消之前的请求
this.abort();
this.controller = new AbortController();
const signal = this.controller.signal;
try {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages }),
signal
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
if (signal.aborted) break;
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') return;
const json = JSON.parse(data);
const content = json.choices?.[0]?.delta?.content;
if (content) onToken(content);
}
}
}
} catch (error) {
if (error.name === 'AbortError') {
console.log('Stream aborted by user');
} else {
throw error;
}
} finally {
this.controller = null;
}
}
abort() {
this.controller?.abort();
}
}
// 使用
const chat = new ChatStream();
document.getElementById('send').onclick = () => {
const output = document.getElementById('output');
output.textContent = '';
chat.stream(
[{ role: 'user', content: 'Hello' }],
(token) => { output.textContent += token; }
);
};
document.getElementById('stop').onclick = () => {
chat.abort();
};5.3 超时控制
javascript
// 方法 1: AbortSignal.timeout() (现代浏览器)
const response = await fetch(url, {
signal: AbortSignal.timeout(30000) // 30秒超时
});
// 方法 2: 手动实现
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
try {
const response = await fetch(url, { signal: controller.signal });
// ...
} finally {
clearTimeout(timeoutId);
}
// 方法 3: 合并多个 signal
const userController = new AbortController();
const timeoutSignal = AbortSignal.timeout(30000);
const response = await fetch(url, {
signal: AbortSignal.any([userController.signal, timeoutSignal])
});