SSE Typed Event Stream Subscription
Subscribe to a typed server-sent event stream with EventSource listeners per event type, auto-reconnect awareness, and clean teardown.
Code
type ProgressEvent = { jobId: string; percent: number };
type DoneEvent = { jobId: string; resultUrl: string };
type AlertEvent = { level: 'warn' | 'error'; text: string };
interface StreamHandlers {
progress: (event: ProgressEvent) => void;
done: (event: DoneEvent) => void;
alert: (event: AlertEvent) => void;
}
function subscribeJobStream(jobId: string, handlers: StreamHandlers) {
const source = new EventSource('/api/jobs/' + jobId + '/stream');
const entries: [keyof StreamHandlers, (e: MessageEvent) => void][] = [
['progress', (e) => handlers.progress(JSON.parse(e.data) as ProgressEvent)],
['done', (e) => {
handlers.done(JSON.parse(e.data) as DoneEvent);
source.close();
}],
['alert', (e) => handlers.alert(JSON.parse(e.data) as AlertEvent)],
];
for (const [type, listener] of entries) {
source.addEventListener(type, listener as EventListener);
}
source.onerror = () => {
// EventSource 自动重连;done 后的断开无需处理
if (source.readyState === EventSource.CLOSED) {
console.log('stream closed by server');
}
};
return () => {
for (const [type, listener] of entries) {
source.removeEventListener(type, listener as EventListener);
}
source.close();
};
}
const teardown = subscribeJobStream('job-7', {
progress: (e) => console.log('progress', e.percent),
done: (e) => console.log('result at', e.resultUrl),
alert: (e) => console.warn(e.level, e.text),
});
teardown();Report a problem with this exampleOpens a pre-filled issue on GitHub.