Last active
September 11, 2019 23:22
-
-
Save lwillmeth/6c29c3ca8a0b65f514f0018cb845e33c to your computer and use it in GitHub Desktop.
This file contains 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 defaultOptions = Object.freeze({ | |
delayMs: 0, // (ms delay on all invocations) | |
retries: 3, // (retry failed promises up to 3 times each) | |
errorDelayMs: 100, // (ms delay after first error) | |
exponential: true, // (if true then double errorDelayMs on consecutive errors) | |
throwOnError: true, // (set to false to only log errors that fail to retry) | |
keepErrors: false, // (set to true to return an Array in `.errors`) | |
logger: console, // (set to initialized lib-logger-nodejs) | |
verbose: process.env.VERBOSE || false, // (set to true to log summary of successes and errors every time) | |
errorMsg: '' // (additional error message to display if all retries fail) | |
}); | |
const NUM_ITEMS = 10; | |
const DELAY = 50; | |
let totalRunTime = 0; | |
async function batchRetry(items, fn, options) { | |
const ops = { ...defaultOptions, ...options }; | |
// build up hashes to maintain original order | |
const successes = {}; | |
const errors = {}; | |
let index = ops.batchSize; | |
async function startPromise(i, item) { | |
// console.log('kicking off item:', item.id); | |
return fn(item) | |
.then(res => successes[i] = res) | |
.catch(err => errors[i] = err) | |
.then(() => { | |
if (index < items.length) { | |
return startPromise(index, items[index++]); | |
} | |
}) | |
} | |
const firstBatch = items.slice(0, ops.batchSize); | |
const activePromises = firstBatch.map((item, i) => startPromise(i, item)); | |
// resolve all promises without bailing if the first batch fails | |
for(let i=0; i<activePromises.length; i++) { | |
await activePromises[i]; | |
} | |
// flatten the hashes into arrays | |
return { successes: Object.values(successes), errors: Object.values(errors) }; | |
} | |
async function waitForSomething(i) { | |
return new Promise((resolve, reject) => { | |
// introduce random failures | |
const result = Math.random() > 0.5 ? resolve : reject; | |
const thisRunTime = DELAY * (1 + Math.random()); // 50-100 ms | |
totalRunTime += thisRunTime; // keep a running total of async thread time just for fun | |
setTimeout(() => result(i), thisRunTime) | |
}); | |
} | |
async function demo(){ | |
const items = []; | |
for (let i = 0; i < NUM_ITEMS; ++i) { | |
items.push({ id: i }); | |
} | |
const startedAt = Date.now(); | |
const results = await batchRetry(items, waitForSomething, { batchSize: 5, retries: 3 }); | |
const timeToRunAll = Date.now() - startedAt; | |
console.log(`Results => ${results.successes.length} successes and ${results.errors.length} failures.`); | |
console.log(`Successes: ${results.successes.map(i => i.id).join(',')}.`); | |
console.log(`Errors: ${results.errors.map(i => i.id).join(',')}.`); | |
console.log(`It took ${timeToRunAll} ms to run ${NUM_ITEMS} items, with a total thread time of ${totalRunTime} ms.`); | |
} | |
demo(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment