Open
Description
Describe the feature
Why
Nowadays, many ai services are provided by http api, the task normally cost long time, http short polling is a simple way to get the result. For example: https://platform.tripo3d.ai/docs/task#polling
Solution
I previous implement it like following:
import EventEmitter from 'events'
import { omit } from 'lodash-es'
import type { Promisable } from 'type-fest'
interface EventData<D extends Record<string, any> = {}> {
start: void
response: D
error: any
stop: void
}
type Event = keyof EventData
type Options = RequestInit & {
interval?: number
pollingTimeout?: number
}
/**
* Http Short Polling
*/
export class HttpShortPoll<D> extends EventEmitter {
url: string
status: 'init' | 'polling' | 'stopped' = 'init'
_pollingTimer: NodeJS.Timeout | null = null
_startTimestamp: number | null = null
options: Options = {
interval: 1000,
// 20 minutes
pollingTimeout: 1000 * 60 * 20,
}
constructor(url: string, options?: Options) {
super()
this.url = url
this.options = { ...this.options, ...options }
}
on<E extends Event>(event: E, listener: (data: EventData<D>[E]) => Promisable<void>) {
return super.on(event, listener)
}
emit<E extends Event>(event: E, data?: EventData<D>[E]) {
return super.emit(event, data)
}
start() {
if (this.status !== 'init') {
return
}
this.status = 'polling'
this.emit('start')
this._startTimestamp = Date.now()
this._pollingTimer = setInterval(async () => {
if (this.status !== 'polling') {
return
}
if (Date.now() - this._startTimestamp > this.options.pollingTimeout) {
this.stop()
return
}
try {
const resp = await fetch(this.url, omit(this.options, ['interval', 'pollingTimeout']))
const json = await resp.json()
this.emit('response', json as D)
} catch (error) {
console.error(error)
this.emit('error', error)
return
}
}, this.options.interval)
}
stop() {
this.status = 'stopped'
if (this._pollingTimer) {
clearInterval(this._pollingTimer)
this._pollingTimer = null
}
this.emit('stop')
}
}
// for example
const poll = new HttpShortPoll('https://api.example.com/data', {
interval: 1000,
pollingTimeout: 10000,
})
poll.on('start', () => {
console.log('Polling started')
})
poll.on('response', (data) => {
console.log('Data received:', data)
})
poll.on('error', (error) => {
console.error('Error:', error)
})
poll.on('stop', () => {
console.log('Polling stopped')
})
Additional information
- Would you be willing to help implement this feature?