core-functions
Advanced tools
Comparing version
@@ -0,0 +0,0 @@ /** |
{ | ||
"name": "core-functions", | ||
"version": "3.0.0", | ||
"version": "3.0.1", | ||
"description": "Core functions, utilities and classes for working with Node/JavaScript primitives and built-in objects, including strings, booleans, Promises, base 64, Arrays, Objects, standard AppErrors, etc.", | ||
@@ -5,0 +5,0 @@ "author": "Byron du Preez", |
109
promises.js
@@ -9,6 +9,8 @@ 'use strict'; | ||
/** | ||
* An Error subclass thrown to cancel/short-circuit a promise that is waiting for a list of promises to resolve. | ||
* An Error subclass thrown to cancel/short-circuit a promise that is waiting for a list of promises to resolve (see | ||
* {@link every}) or for a chained list of promise-returning function calls with inputs to resolve (see {@link chain}). | ||
* @property (Success|Failure)[]} resolvedOutcomes - a list of resolved outcomes | ||
* @property {Promise[]} unresolvedPromises - a list of unresolved promises | ||
* @property {boolean} completed - whether all of the promises resolved prior to cancellation or not | ||
* @property {Promise[]|*[]} unresolvedInputs - a list of unresolved inputs or promises | ||
* @property {Promise[]|*[]} unresolvedPromises - an alias for unresolvedInputs (for backward-compatibility) | ||
* @property {boolean} completed - whether all of the promises or chained function calls resolved prior to cancellation or not | ||
*/ | ||
@@ -19,8 +21,9 @@ class CancelledError extends Error { | ||
* @param {(Success|Failure)[]} resolvedOutcomes - the list of resolved outcomes | ||
* @param {Promise[]} unresolvedPromises - the list of unresolved promises | ||
* @param {*[]|Promise[]} unresolvedInputs - the list of unresolved inputs (or unresolved promises) | ||
*/ | ||
constructor(resolvedOutcomes, unresolvedPromises) { | ||
constructor(resolvedOutcomes, unresolvedInputs) { | ||
const doneCount = resolvedOutcomes ? resolvedOutcomes.length : 0; | ||
const totalCount = doneCount + (unresolvedPromises ? unresolvedPromises.length : 0); | ||
const message = 'Cancelled' + (totalCount > 0 ? ` after resolving ${doneCount} outcome${doneCount !== 1 ? 's' : ''} of ${totalCount} promise${totalCount !== 1 ? 's' : ''}` : ''); | ||
const incompleteCount = unresolvedInputs ? unresolvedInputs.length : 0; | ||
const totalCount = doneCount + incompleteCount; | ||
const message = 'Cancelled' + (resolvedOutcomes ? ` after resolving ${doneCount} outcome${doneCount !== 1 ? 's' : ''}${unresolvedInputs ? ` of ${totalCount} input${totalCount !== 1 ? 's' : ''}` : ''}` : ''); | ||
super(message); | ||
@@ -30,4 +33,6 @@ Object.defineProperty(this, 'message', {writable: false, enumerable: true, configurable: false}); | ||
Object.defineProperty(this, 'resolvedOutcomes', {value: resolvedOutcomes, enumerable: false}); | ||
Object.defineProperty(this, 'unresolvedPromises', {value: unresolvedPromises, enumerable: false}); | ||
Object.defineProperty(this, 'completed', {value: doneCount === totalCount, enumerable: false}); | ||
Object.defineProperty(this, 'unresolvedInputs', {value: unresolvedInputs, enumerable: false}); | ||
Object.defineProperty(this, 'completed', {value: unresolvedInputs ? incompleteCount === 0 : undefined, enumerable: false}); | ||
// Alias for unresolvedInputs for backward-compatibility | ||
Object.defineProperty(this, 'unresolvedPromises', {value: unresolvedInputs, enumerable: false}); | ||
} | ||
@@ -62,2 +67,4 @@ } | ||
toPromise: toPromise, | ||
/** Returns a promise that will return a list of Success or Failure outcomes generated by sequentially chaining calls to the given function `f` with each successive element of the given array of `elements` */ | ||
chain: chain, | ||
/** An Error subclass thrown to cancel/short-circuit a promise that is waiting for a list of promises to resolve. */ | ||
@@ -343,3 +350,3 @@ CancelledError: CancelledError | ||
if (!Array.isArray(promises)) { | ||
throw new Error('The `every` function only accepts an array of promises and/or non-promises'); | ||
throw new Error('The `every` function only accepts `promises` as an array of promises and/or non-promises'); | ||
} | ||
@@ -396,3 +403,3 @@ const n = promises.length; | ||
outcomes[i] = new Failure(err); | ||
// If still not at the last element, then continue with a recursive call; otherwise return results, since done | ||
// If still not at the last element, then continue with a recursive call; otherwise return outcomes, since done | ||
if (i < last) { | ||
@@ -427,1 +434,81 @@ // Short-circuit if cancelled by throwing a cancelled error with the outcomes collected so far | ||
} | ||
/** | ||
* Returns a promise that will return a list of Success or Failure outcomes generated by sequentially chaining calls to | ||
* the given promise-returning function `f` with each successive input from the given array of `inputs`, such that the | ||
* nth call will only be applied to the nth input after the promise of the nth - 1 call has resolved. The list of | ||
* outcomes returned will be in the same sequence and have the same size as the given list of `inputs`. If any | ||
* non-null object is passed as the `cancellable` argument, then this function will also install a `cancel` method on it, | ||
* which expects no arguments and returns true if the chain has already completed; or false otherwise. If this `cancel` | ||
* method is subsequently invoked, it will attempt to prematurely cancel the `chain` of calls by instead throwing a | ||
* `CancelledError`, which will contain a list of any resolved outcomes and any unresolved inputs and which will result | ||
* in a rejected promise being returned. | ||
* | ||
* Note: The returned promise should NEVER reject (UNLESS it is cancelled via the `cancellable`), since it only resolves | ||
* with Success or Failure outcomes, which indicate whether the function `f` succeeded or failed for their corresponding | ||
* input from the given list of `inputs`. | ||
* | ||
* @param {function(input: *, index: number, inputs: *[]): (Promise.<*>|*)} f - a function that must, at least, accept a | ||
* single input to be processed, but can optionally also accept: the index at which the input appears in the given list | ||
* of `inputs`; and the given `inputs` too, and ideally returns a promise that will resolve with the result returned or | ||
* reject with the error thrown | ||
* @param {*[]} inputs - the list of inputs to be passed one at a time to the given function | ||
* @param {Object|Cancellable|*} [cancellable] - an arbitrary object onto which a `cancel` method will be installed | ||
* @returns {Promise.<(Success|Failure)[]|CancelledError>} a promise that will resolve with a list of Success or Failure | ||
* outcomes (unless cancelled); otherwise reject with a `CancelledError` (if cancelled) | ||
* @throws {Error} an error if the given `f` is not a function or the given `inputs` is not an array | ||
*/ | ||
function chain(f, inputs, cancellable) { | ||
if (typeof f !== "function") { | ||
throw new Error('The `chain` function only accepts `f` as a function'); | ||
} | ||
if (!Array.isArray(inputs)) { | ||
throw new Error('The `chain` function only accepts `inputs` as an array'); | ||
} | ||
const n = inputs.length; | ||
if (n <= 0) { | ||
return Promise.resolve([]); | ||
} | ||
const last = n - 1; | ||
const outcomes = new Array(n); | ||
let completed = false; | ||
let cancelled = false; | ||
// Set up a cancel method on the given cancellable object (if any) | ||
if (cancellable && typeof cancellable === 'object') { | ||
cancellable.cancel = () => { | ||
cancelled = true; | ||
return completed; | ||
} | ||
} | ||
function next(i) { | ||
return attempt(() => f(inputs[i], i, inputs)).then( | ||
value => { | ||
outcomes[i] = new Success(value); | ||
// If still not at the last input, then continue with a recursive call; otherwise return outcomes, since done | ||
if (i < last) { | ||
// Short-circuit if cancelled by throwing a cancelled error with the outcomes collected so far | ||
if (cancelled) throw new CancelledError(outcomes.slice(0, i + 1), inputs.slice(i + 1)); | ||
return next(i + 1); | ||
} | ||
completed = true; | ||
return outcomes; | ||
}, | ||
err => { | ||
outcomes[i] = new Failure(err); | ||
// If still not at the last input, then continue with a recursive call; otherwise return outcomes, since done | ||
if (i < last) { | ||
// Short-circuit if cancelled by throwing a cancelled error with the outcomes collected so far | ||
if (cancelled) throw new CancelledError(outcomes.slice(0, i + 1), inputs.slice(i + 1)); | ||
return next(i + 1); | ||
} | ||
completed = true; | ||
return outcomes; | ||
} | ||
); | ||
} | ||
// Start the process at the first input | ||
return next(0); | ||
} |
@@ -1,2 +0,2 @@ | ||
# core-functions v3.0.0 | ||
# core-functions v3.0.1 | ||
@@ -157,2 +157,15 @@ Core functions, utilities and classes for working with Node/JavaScript primitives and built-in objects, including | ||
### 3.0.1 | ||
- Changes to `promises` module: | ||
- Renamed `CancelledError` constructor `unresolvedPromises` parameter to more generic `unresolvedInputs` | ||
- Added `unresolvedInputs` property to `CancelledError` class and kept `unresolvedPromises` as an alias for it | ||
- Fixed bug in `completed` property of `CancelledError` class, which was incorrectly reporting completed as true when | ||
no unresolved inputs or promises were provided | ||
- Added new `chain` function | ||
- Changes to `strings` module: | ||
- Fixed potential shared global regular expression issues in `cleanInspectedPromise` function | ||
- Changes to `type-defs` module: | ||
- Added `Outcome` & `Outcomes` type definitions | ||
- Deleted arbitrary `copy.sh` script | ||
### 3.0.0 | ||
@@ -159,0 +172,0 @@ - Non-backward compatible changes & fixes to `promises.js` module: |
@@ -337,3 +337,6 @@ 'use strict'; | ||
function cleanInspectedPromise(inspectedPromise) { | ||
return inspectedPromise.replace(breakRegex, ' ').replace(promiseInspectRegex, '$1 $3 $5'); //.replace(/"/g, '\\"').replace(/\\'/g, '"') | ||
breakRegex.lastIndex = 0; //NB: MUST RESET lastIndex to zero for global regular expressions (i.e. /.../g )! | ||
const promiseText = inspectedPromise.replace(breakRegex, ' ').replace(promiseInspectRegex, '$1 $3 $5'); | ||
breakRegex.lastIndex = 0; //NB: MUST RESET lastIndex to zero for global regular expressions (i.e. /.../g )! | ||
return promiseText; | ||
} |
{ | ||
"name": "core-functions-tests", | ||
"version": "3.0.0", | ||
"version": "3.0.1", | ||
"author": "Byron du Preez", | ||
@@ -5,0 +5,0 @@ "license": "Apache-2.0", |
@@ -12,2 +12,8 @@ /** | ||
*/ | ||
/** | ||
* @typedef {Success|Failure} Outcome - represents a Success or Failure outcome | ||
*/ | ||
/** | ||
* @typedef {(Success|Failure)[]} Outcomes - represents a list of Success or Failure outcomes | ||
*/ | ||
@@ -14,0 +20,0 @@ /** |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
635618
4.43%10011
4.15%349
3.87%