class Event {
constructor() {
this.eventMap = {}
}
on(type, listener) {
this.eventMap[type] = (this.eventMap[type] || []).concat(listener)
}
emit(type) {
if (!this.eventMap[type]) return
this.eventMap[type].forEach((listener) => {
listener()
})
}
off(type, listener) {
if (!this.eventMap[type]) return
this.eventMap[type] = this.eventMap[type].filter((fn) => fn !== listener && fn !== listener.onceHandler)
}
once(type, listener) {
const handler = () => {
listener()
this.off(type, handler)
}
listener.onceHandler = handler
this.on(type, handler)
}
}
class ApiLimitControl extends Event {
constructor(max) {
super()
this.max = max
this.isRunning = 0
this.queue = []
this.id = 0
}
register(promiseFn) {
const id = ++this.id
const p = new Promise((resolve) => {
this.once(id, () => {
Promise.resolve(promiseFn()).then((res) => {
if (!this.queue.length) {
this.isRunning--
} else {
this.emit(this.queue.shift())
}
resolve(res)
})
})
})
if (this.isRunning < this.max) {
this.isRunning++
this.emit(id)
} else {
this.queue.push(id)
}
return p
}
}
const apiLimitController = new ApiLimitControl(2)
const mockAsync = (timeout) => new Promise((resolve) => {
setTimeout(() => {
resolve(timeout)
}, timeout * 1000)
})
apiLimitController.register(() => mockAsync(1))
apiLimitController.register(() => mockAsync(1))
apiLimitController.register(() => mockAsync(2))