Skip to content

Instantly share code, notes, and snippets.

@DKurilo
Last active September 26, 2023 13:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DKurilo/36e93c348f082b35b1e1384c698ee7d4 to your computer and use it in GitHub Desktop.
Save DKurilo/36e93c348f082b35b1e1384c698ee7d4 to your computer and use it in GitHub Desktop.
Simple TypeScript and JavaScript queues
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,
);
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