Skip to content

Instantly share code, notes, and snippets.

@teidesu
Created June 22, 2021 13:11
Show Gist options
  • Save teidesu/d2ebd40aaf3e7aff2a041871081a2c0b to your computer and use it in GitHub Desktop.
Save teidesu/d2ebd40aaf3e7aff2a041871081a2c0b to your computer and use it in GitHub Desktop.
Promise.race but error-tolerant
/**
* Similar to Promise.race(), but resolves once one of the promises
* resolve to a successful value and didn't throw error.
* A promise result is considered `successful` when
* check(result) resolves to a truthy value.
*
* When all promises resulted with unsuccessful values, `null` is returned,
* otherwise result of first successful one is returned
*
* @param proms List of promises
* @param checker (optional) Function which is used to determine whether value is successful
* @param errorHandler (optional) Function which will be called each time one of promises throws an error
*/
function successRace (proms, checker = e => !!e, errorHandler = null) {
return new Promise((resolve) => {
Promise.all(proms.map((prom) =>
prom.then((res) => {
if (checker(res)) resolve(res)
}).catch(errorHandler || (() => {}))
)).then(() => resolve(null))
})
}
async function test () {
const sleep = ms => new Promise((resolve) => setTimeout(resolve, ms))
function falsy () {
return Promise.resolve({
ok: false
})
}
function falsyErr () {
return Promise.reject(Error('error'))
}
function falsyErrDelayed () {
return sleep(100).then(() => {
throw Error('error')
})
}
function falsyDelayed () {
return sleep(100).then(() => ({
ok: false
}))
}
function truthy () {
return sleep(150).then(() => ({
ok: true,
value: 'truthy()'
}))
}
function truthyDelayed () {
return sleep(200).then(() => ({
ok: true,
value: 'truthyDelayed()'
}))
}
const eql = (a, b) => {
if (a !== b) throw new Error(`Expected ${a} to equal ${b}`)
}
let res
res = await successRace([], i => i.ok)
eql(res, null)
res = await successRace([falsy(), truthy()], i => i.ok)
eql(res.value, 'truthy()')
res = await successRace([falsy(), falsyDelayed(), truthyDelayed()], i => i.ok)
eql(res.value, 'truthyDelayed()')
res = await successRace([falsy(), falsyDelayed(), truthy(), truthyDelayed()], i => i.ok)
eql(res.value, 'truthy()')
res = await successRace([falsy(), falsyDelayed(), falsyDelayed()], i => i.ok)
eql(res, null)
let errCalled = false
let errHandler = () => errCalled = true
res = await successRace([falsyErr(), truthyDelayed()], i => i.ok, errHandler)
eql(res.value, 'truthyDelayed()')
eql(errCalled, true)
errCalled = false
res = await successRace([falsyErr(), falsyErrDelayed(), truthyDelayed()], i => i.ok, errHandler)
eql(res.value, 'truthyDelayed()')
eql(errCalled, true)
console.log('tests ok!')
}
test().catch(console.error)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment