| name | websocket |
| description | WebSocket 连接生命周期管理:何时关闭、如何关闭、如何通过 AbortSignal 支持外部取消。Use when implementing WebSocket subscriptions, real-time listeners, or any long-lived WebSocket connections. |
WebSocket 连接资源管理
1. WebSocket 在什么时候关闭
WebSocket 连接必须在以下任一情况发生时立即关闭,避免连接泄漏和资源浪费:
| 场景 | 说明 |
|---|
| 收到预期数据 | 订阅目的达成(如收到目标事件),立即 ws.close() |
| 超时 | 达到预设超时时间,尚未收到预期数据,关闭并结束等待 |
| 连接错误 | error 事件触发,关闭连接并 reject/降级 |
| 外部取消 | 调用方不再需要结果(如组件卸载、用户离开),应能主动中止 |
前三种通常已在实现中处理;外部取消最容易遗漏,导致 Dialog 关闭、页面切换时连接仍保持到超时。
2. 如何关闭
function closeWebSocket(ws: WebSocket, timeoutId: NodeJS.Timeout | number) {
clearTimeout(timeoutId)
ws.close()
}
要点:
- 关闭前先
clearTimeout,否则超时回调可能仍会执行
- 关闭后不要再
resolve/reject,避免重复结算
- 多次
ws.close() 是幂等的,但应避免在已关闭后继续操作
3. 如何取消:使用 AbortSignal
支持外部取消的标准做法是接受 AbortSignal 参数:
interface ReceiptWatchOptions {
recipientAddress: string
tokenAddress: string
timeoutMs: number
signal?: AbortSignal
}
async function watchForReceipt(options: ReceiptWatchOptions): Promise<ReceiptWatchResult> {
const { signal, timeoutMs } = options
return new Promise((resolve, reject) => {
const ws = new WebSocket(URL)
const cleanup = () => {
clearTimeout(timeoutId)
ws.close()
}
const timeoutId = setTimeout(() => {
cleanup()
resolve({ received: false })
}, timeoutMs)
if (signal) {
if (signal.aborted) {
cleanup()
reject(new DOMException('Aborted', 'AbortError'))
return
}
signal.addEventListener('abort', () => {
cleanup()
reject(new DOMException('Aborted', 'AbortError'))
})
}
ws.addEventListener('error', () => {
cleanup()
reject(new Error('WebSocket 连接失败'))
})
ws.addEventListener('message', (event) => {
if () {
cleanup()
resolve({ received: true, txHash: '...' })
}
})
})
}
调用方用法(React 示例)
function useDeposit() {
const abortRef = useRef<AbortController | null>(null)
const deposit = useCallback(async () => {
const ac = new AbortController()
abortRef.current = ac
try {
const result = await verifyDeposit({
signal: ac.signal,
})
} finally {
abortRef.current = null
}
}, [])
useEffect(() => {
return () => {
abortRef.current?.abort()
}
}, [])
return { deposit }
}
4. 核心原则
- WebSocket 是有状态资源,必须显式关闭,不能依赖 GC
- 支持
AbortSignal 的异步函数,在组件/流程销毁时应传递 signal 并在 unmount 时 abort()
- 关闭时要统一清理:
clearTimeout + ws.close(),避免重复 resolve/reject
- 超时、错误、成功、取消——四条路径都要走到清理逻辑