core-functions
Advanced tools
Comparing version 3.0.0 to 3.0.1
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
635618
10011
349