Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Rowadz/87cbb414853baf7a511b088394f51994 to your computer and use it in GitHub Desktop.
Save Rowadz/87cbb414853baf7a511b088394f51994 to your computer and use it in GitHub Desktop.
race condition of promises with a stopping condition
import axios, { AxiosResponse } from 'axios'
import { v4 as uuidv4 } from 'uuid'
export type PromiseStatus<T> = {
promise: Promise<T>
isFulfilled: boolean
isRejected: boolean
}
export type TODO = {
userId: number
id: number
title: string
completed: boolean
}
export type PromiseMap<T> = Map<string, PromiseStatus<T>>
declare global {
interface PromiseConstructor {
id: string
raceWithCondition: <T>(
promises: Promise<T>[],
fun: (value: T) => boolean
) => Promise<T>
}
}
Promise.raceWithCondition = async <T>(
promises: Promise<T>[],
fun: (value: T) => boolean
): Promise<T> => {
const map: PromiseMap<T> = new Map<string, PromiseStatus<T>>()
const mappedPromises: [string, Promise<T>][] = promises.map((p) => {
const id = uuidv4()
p = p
.then((value: T) => {
const obj = map.get(id)!
obj.isFulfilled = true
return value
})
.catch((error: any) => {
const obj = map.get(id)!
obj.isRejected = true
return error
})
map.set(id, { promise: p, isRejected: false, isFulfilled: false })
return [id, p]
})
const result = await Promise.race(mappedPromises.map(([_, p]) => p))
if (!(result instanceof Error) && fun(result)) {
return result
}
const nextCallPromises: Promise<T>[] = mappedPromises
.filter(([id]) => {
const obj = map.get(id)!
return !(obj.isFulfilled || obj.isRejected)
})
.map(([_, p]) => p)
return Promise.raceWithCondition(nextCallPromises, fun)
}
const DEFAULT_TIMEOUT = 200
const fetchData = async (id: number): Promise<AxiosResponse<TODO>> => {
// simulate a delay
if (id === 1) {
await new Promise((resolve) => setTimeout(resolve, DEFAULT_TIMEOUT + 1))
}
// simulate an error
if (id === 2) {
throw new Error('')
}
// simulate a rejection in the promise
if (id === 3) {
return Promise.reject('')
}
return axios.get<TODO>(`https://jsonplaceholder.typicode.com/todos/${id}`)
}
const main = async () => {
const promises = [
fetchData(1),
fetchData(2),
fetchData(3),
fetchData(4),
fetchData(5),
fetchData(6),
]
const finalData = await Promise.raceWithCondition<AxiosResponse<TODO>>(
promises,
(value: AxiosResponse<TODO>) => {
return value && value.data.id === 1
}
).then((response) => response.data)
return finalData
}
main().then(console.log)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment