UNPKG

neverthrow-result-utils

Version:

Extra utilities for working with Neverthrow results

146 lines 6.56 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResultUtils = void 0; const delay_1 = __importDefault(require("delay")); const neverthrow_1 = require("neverthrow"); class ResultUtils { static combine(asyncResultList) { return neverthrow_1.ResultAsync.combine(asyncResultList); } /** * Given a list of ResultAsync, it will return the result only from the * first that completes. * @param asyncResultList * @returns */ static race(asyncResultList) { return neverthrow_1.ResultAsync.fromPromise(Promise.race(asyncResultList), (e) => { return e; }).andThen((result) => { return result.isErr() ? (0, neverthrow_1.err)(result.error) : (0, neverthrow_1.ok)(result.value); }); } static executeSerially(funcList) { const executor = async () => { const results = new Array(); for (const func of funcList) { const result = await func(); if (result.isErr()) { throw result.error; } else { results.push(result.value); } } return results; }; return neverthrow_1.ResultAsync.fromPromise(executor(), (e) => { return e; }); } /** * backoffAndRetry() is a powerful method that executes a function that returns * a ResultAsync. If that result is OK, then the result is returned directly. * If it is an error, it will check to see if the returned error is in the * acceptableErrors list. If so, it will delay and re-execute the function. * If it's not an acceptable error type, the error result is returned. * Each failure will backoff exponentially. * * This is an excellent wrapper for functions that can fail inconsistently * in some ways were we expect a retry to succeed. For example, a function * that communicates with a database over a sketchy connection might just need * to retry in a few seconds. * @param func * @param acceptableErrors A list of error types that are considered retryable. Logical error types that will never succeed should not be in this list, but error types that represent temporary runtime errors should. * @param maxAttempts This is the maximum number of times a retry will be attempted; set to null to use infinite retries. * @param baseSeconds This is the delay in seconds for the first retry attempt. It will double with each successive retry. * @returns */ static backoffAndRetry(func, acceptableErrors, maxAttempts, baseSeconds = 5) { if (maxAttempts != null && maxAttempts < 1) { throw new Error("maxAttempts must be 1 or more!"); } if (baseSeconds < 1) { throw new Error("baseSeconds must be 1 or more!"); } const runAndCheck = (currentAttempt, nextAttemptSecs, lastError) => { if (maxAttempts != null && currentAttempt > maxAttempts) { if (lastError == null) { throw new Error("Error before first function run; logical error! maxAttempts must be 1 or more!"); } return (0, neverthrow_1.errAsync)(lastError); } // Check the result. If it's not an error, we're done! // If it's an error, check the error type against acceptableErrors. If it's in the list, // wait some amount of time and try again. // If it is not in the list, return the error and stop. return func().orElse((e) => { let retry = false; for (const acceptableError of acceptableErrors) { if (e instanceof acceptableError) { retry = true; break; } } if (retry) { return neverthrow_1.ResultAsync.fromSafePromise((0, delay_1.default)(nextAttemptSecs)).andThen(() => { return runAndCheck(++currentAttempt, nextAttemptSecs * 2, e); }); } return (0, neverthrow_1.errAsync)(e); }); }; return runAndCheck(1, baseSeconds, null); } /** * filter() is a normal filter method that works with async callbacks. * This works like a nomral array filter() call, except the callback returns * a ResultAsync<boolean> instead of just boolean. * @param arr the source array * @param callback a function that returns a ResultAsync<boolean>; if it returns true then the source value is included in the result. * @returns a ResultAsync containing an array of source values where the callback returns true. */ static filter(arr, callback) { const filterVals = new Array(); return ResultUtils.combine(arr.map((val) => { return callback(val).map((result) => { if (result) { filterVals.push(val); } }); })).map(() => { return filterVals; }); } /** * map() is a way to combine multiple async callbacks. * This works like a normal array map() call, except the callbacks return ResultAsync. * It will combine all the results and return a single ResultAsync with an array of the mapped * values. * @param arr The source array of objects * @param callback A function that returns a ResultAsync and a value * @returns A ResultAsync containing an array of the mapped values or the first error. */ static map(arr, callback) { const mapVals = new Array(); return ResultUtils.combine(arr.map((val) => { return callback(val).map((result) => { mapVals.push(result); }); })).map(() => { return mapVals; }); } static delay(ms) { return neverthrow_1.ResultAsync.fromSafePromise(new Promise((resolve) => { setTimeout(() => { resolve(); }, ms); })); } } exports.ResultUtils = ResultUtils; //# sourceMappingURL=index.js.map