UNPKG

@speckle/shared

Version:

Shared code between various Speckle JS packages

70 lines 3.65 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.batchAsyncOperations = batchAsyncOperations; /* eslint-disable @typescript-eslint/no-explicit-any */ const _lodash_1 = require("#lodash"); /** * Utility for batching async operations. Useful when you have thousands of async operations and you can't * just invoke them all at once because of resource constraints (e.g. network bandwidth). * * 'operationParams' should be an array of parameters for each async operation. The size of this array coresponds * to the amount of async operations that will be invoked. * 'operationPromiseGenerator' will be invoked sequentially with each params of 'operationParams' and with it * you can specify what the actual async operation should be * * TODO: Some tests would be nice, although it does work when tested through `yarn cli download commit` in speckle-server */ async function batchAsyncOperations(name, operationParams, operationPromiseGenerator, options) { const { retryCount = 3, batchSize = 100, dropReturns = false, logger = (...args) => console.log(...args) } = options || {}; const finalLogger = (message, ...args) => logger(`[${name}] ${message}`, ...args); finalLogger('Starting batched operation...'); const operationCount = operationParams.length; let allResults = []; let executedOperationCount = 0; const batchCount = Math.ceil(operationCount / batchSize); for (let i = 0; i < batchCount; i++) { finalLogger(`Processing batch ${i + 1} out of ${batchCount}...`); // Figure out how many operations we can execute in this batch const newExecutedOperationCount = Math.min(executedOperationCount + batchSize, operationCount); const operationsToExecuteCount = newExecutedOperationCount - executedOperationCount; if (operationsToExecuteCount <= 0) return; // Invoke operation generator const batchParams = operationParams.slice(executedOperationCount, newExecutedOperationCount); const batchRequests = []; let currentOperationIdx = executedOperationCount; for (const params of batchParams) { const currentOperationNumber = currentOperationIdx + 1; const label = `${currentOperationNumber}/${operationCount}`; const operationPromise = (async () => { finalLogger(`Queuing operation ${label}...`); const execute = () => operationPromiseGenerator(params); let promise = execute().then((res) => { finalLogger(`...finished operation ${label}`); return res; }); // Attach retries (0, _lodash_1.range)(retryCount).forEach((retry) => { promise = promise.catch((e) => { finalLogger(`...failure in operation ${label}: "${e}". Triggering retry ${retry + 1}...`); return execute(); }); }); promise = promise.catch((e) => { finalLogger(`...final failure in operation ${label}!`); throw e; }); return promise; })(); batchRequests.push(operationPromise); currentOperationIdx++; } const batchResults = await Promise.all(batchRequests); if (!dropReturns) { allResults = allResults.concat(batchResults); } executedOperationCount = newExecutedOperationCount; } return dropReturns ? true : allResults; } //# sourceMappingURL=batch.js.map