Last active
September 26, 2023 13:28
-
-
Save DKurilo/36e93c348f082b35b1e1384c698ee7d4 to your computer and use it in GitHub Desktop.
Simple TypeScript and JavaScript queues
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const mkQueue = (sameTimeCount, delay, maxRepeats) => { | |
const queued = []; | |
const inWork = []; | |
const removeFromInWork = (frr) => { | |
const i = inWork.indexOf(frr); | |
inWork.splice(i, 1); | |
if (inWork.length < sameTimeCount && queued.length > 0) { | |
const [newFrr] = queued.splice(0, 1); | |
// eslint-disable-next-line no-use-before-define | |
addToInWork(newFrr); | |
} | |
}; | |
const addToInWork = (frr) => { | |
inWork.push(frr); | |
const { f, resolve, reject } = frr; | |
setTimeout( | |
() => | |
f() | |
.then((r) => { | |
removeFromInWork(frr); | |
resolve(r); | |
}) | |
.catch((e) => { | |
removeFromInWork(frr); | |
if (frr.repeats + 1 < maxRepeats) { | |
// eslint-disable-next-line no-use-before-define | |
addJob({ ...frr, repeats: frr.repeats + 1 }); | |
return; | |
} | |
reject(e); | |
}), | |
delay, | |
); | |
}; | |
const addJob = (frr) => { | |
if (inWork.length < sameTimeCount) { | |
addToInWork(frr); | |
} else { | |
queued.push(frr); | |
} | |
}; | |
const queuedF = (f) => | |
new Promise((resolve, reject) => { | |
addJob({ | |
f, | |
resolve, | |
reject, | |
repeats: 0, | |
}); | |
}); | |
return queuedF; | |
}; | |
// Example / Test | |
const good = (t, res) => | |
new Promise((resolve) => { | |
setTimeout(() => { | |
resolve(res); | |
}, t); | |
}).then((x) => { | |
console.log(x); | |
return x; | |
}); | |
const bad = (t, mkErr) => | |
new Promise((_resolve, reject) => { | |
setTimeout(() => { | |
reject(mkErr()); | |
}, t); | |
}).catch((err) => { | |
console.log(err); | |
throw err; | |
}); | |
const queue = mkQueue(2, 100, 3); | |
Promise.all([ | |
queue(() => good(1000, 1)), | |
queue(() => good(1000, 2)), | |
queue(() => good(100, 3)), | |
queue(() => good(50, 4)), | |
]).then(console.log); | |
Promise.all([queue(() => bad(100, () => new Error('oops 1'))), queue(() => bad(50, () => new Error('oops 2')))]).catch( | |
console.log, | |
); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Job<T> = { | |
f: () => Promise<T>; | |
resolve: (value: T | PromiseLike<T>) => void; | |
reject: (reason?: unknown) => void; | |
repeats: number; | |
}; | |
export const mkQueue = ( | |
sameTimeCount: number, | |
delay: number, | |
maxRepeats: number | |
): (<T>(f: () => Promise<T>) => Promise<T>) => { | |
const queued: Job<any>[] = []; | |
const inWork: Job<any>[] = []; | |
const removeFromInWork = <T>(frr: Job<T>): void => { | |
const i = inWork.indexOf(frr); | |
inWork.splice(i, 1); | |
if (inWork.length < sameTimeCount && queued.length > 0) { | |
const [newFrr] = queued.splice(0, 1); | |
// eslint-disable-next-line @typescript-eslint/no-use-before-define | |
addToInWork(newFrr); | |
} | |
}; | |
const addToInWork = <T>(frr: Job<T>): void => { | |
inWork.push(frr); | |
const { f, resolve, reject } = frr; | |
f() | |
.then((r) => { | |
removeFromInWork(frr); | |
resolve(r); | |
}) | |
.catch((e) => { | |
removeFromInWork(frr); | |
if (frr.repeats + 1 < maxRepeats) { | |
setTimeout(() => { | |
// eslint-disable-next-line @typescript-eslint/no-use-before-define | |
addJob({ ...frr, repeats: frr.repeats + 1 }); | |
}, delay); | |
return; | |
} | |
reject(e); | |
}); | |
}; | |
const addJob = <T>(frr: Job<T>): void => { | |
if (inWork.length < sameTimeCount) { | |
addToInWork(frr); | |
} else { | |
queued.push(frr); | |
} | |
}; | |
const queuedF = <T>(f: () => Promise<T>): Promise<T> => | |
new Promise((resolve, reject) => { | |
addJob({ | |
f, | |
resolve, | |
reject, | |
repeats: 0, | |
}); | |
}); | |
return queuedF; | |
}; | |
// Tests and usage | |
const good = (t, res) => | |
new Promise((resolve) => { | |
setTimeout(() => { | |
resolve(res); | |
}, t); | |
}).then((x) => { | |
console.log(x); | |
return x; | |
}); | |
const bad = (t, mkErr) => | |
new Promise((_resolve, reject) => { | |
setTimeout(() => { | |
reject(mkErr()); | |
}, t); | |
}).catch((err) => { | |
console.log(err); | |
throw err; | |
}); | |
const queue = mkQueue(2, 100, 3); | |
Promise.all([ | |
queue(() => good(1000, 1)), | |
queue(() => good(1000, 2)), | |
queue(() => good(100, 3)), | |
queue(() => good(50, 4)), | |
]).then(console.log); | |
Promise.all([queue(() => bad(100, () => new Error('oops 1'))), queue(() => bad(50, () => new Error('oops 2')))]).catch( | |
console.log, | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment