Module: util/function

Various utility functions, some of them, e.g. flat() or memoize(), generic and not ONE specific, so that they could be used for other purposes too.

Source:

Methods

(static) createNeverFailAsyncErrorWrapper(onError) → {NeverFailAsyncErrorWrapper}

Parameters:
Name Type Description
onError function

Callback function to receive the error object

This function creates an asynchronous wrapper (function) that catches exceptions thrown by the function and reports them through a callback function. Optionally it rethrows the intercepted exception.

Two different use cases

  1. A t-pipe (like the "tee" Unix command) where we intercept the exception to send it to a 3rd party that otherwise would not see the error through a callback function, but then rethrow the exception so that the function's parent (caller) does not notice the interception.

  2. Functions that run decoupled from the code that were started through setTimeout, for example. They have no parent, if they throw the exception would immediately end up with the runtime. The wrapper catches the exception and redirects the error object to a callback.

Explanation

The structure of this function is a function that returns a function that returns a function:

((Error) => void, ?boolean) => (Function) => (...args: any[]) => any

The first function takes a callback to be invoked each time there is an error, which receives the Error object as its single argument.

  1. The second function receives the function that should be wrapped.

  2. The first function is the wrapped function, which is used in place of the original function.

  3. Those three steps are potentially located in very different places/modules.

Functions returning functions are a method to cross-connect different places in modules independent of their lexical hierarchy. Imagine a traveler wandering to different places, collecting data and learning new methods, and at the end, after coming back home, putting it all together, using the new methods learned on the journey and the information collected in the many places they visited to create something new in the final location.

Our scenario is that you have one error callback function to collect errors from several functions that you don't invoke but that you control. For example, our chum-exporter service functions are even invoked from an external 3rd party, but the exporter as the "manager" of the "employees" (the service functions) should be informed, not the "customer" who placed an order, i.e. the remote instance.

That single callback is used to collect errors from several functions, and each of the functions are expected to be called many times.

Our scenario would look like this:

  1. ONE TIME: The onError callback is provided (you get a new function for step 2). Since this step is performed only once you get one partially applied function back that can now be used for all service functions.

    // Create a wrapper and an error callback function one time
    const errorWrapper = FunctionUtils.createNeverFailAsyncErrorWrapper(
        error => console.log(error)
    );
    
  2. MANY TIMES: You can use the error wrapper function created in step 1. many times, for as many functions as you like. The function that is to be watched for errors is provided as the single argument for the 2nd step. This yields a wrapped function that functions just like an unwrapped one, except that errors are sent to the onError function provided in step 1., after which they are rethrown if rethrow was set to true.

    // Apply the wrapper to many functions
    const wrappedFunction1 = errorWrapper(myFunction1);
    const wrappedFunction2 = errorWrapper(myFunction2);
    
  3. MANY MANY(!) TIMES: The respective wrapped function is called many times (in our chum-exporter service function example, to respond to requests by the remote instance, e.g. to send a file). Only if there is an error does the presence of the wrapper around the original function have any impact.

    // Use the wrapped functions many, many times
    myArray1.map(val => wrappedFunction1(val));
    ...
    const someResult1 = wrappedFunction2(...arguments1);
    const someResult2 = wrappedFunction2(...arguments2);
    

Example of usage in One

Module chum-exporter's service functions provided to a remote instance through a websocket-promisifier controlled connection. The caller of the function is the websocket-promisifier, which when it gets a rejected promise returns a generic error message to the remote instance. We create a T-pipe like mechanism to also send the error to the main exporter function's promise, which in turn is used to inform the overall Chum parent module that a service requested by the remote instance had a problem.

Source:
Returns:

Returns a NeverFailAsyncErrorWrapper function that takes a function as argument, which is then wrapped by a try/catch and returns a NeverFailAsyncErrorWrappedFn function. The wrapped function returns the return value of the function it wraps or undefined in case of an error, unless rethrow is true.

Type: NeverFailAsyncErrorWrapper

(static) createRethrowingAsyncErrorWrapper(onError) → {RethrowingAsyncErrorWrapper}

Parameters:
Name Type Description
onError function

Callback function to receive the error object

This function is almost the same as createNeverFailAsyncErrorWrapper -- but the vital difference is that this wrapper 1) rethrows the error and 2) on success returns the value returned by the wrapped function, instead of always returning undefined no matter what.

This version of the error wrapper is meant for scenarios where the error handling and the invocation belong to different scopes. An example is the chum-exporter: It's service functions are invoked by the websocket-promisifier reacting to network requests, but exceptions should be handled in the context of the chum-exporter. The calling modules still wants to receive all the values, and at least know if there is an exception to inform the remote instance, even if it does not do any error handling for it.

Source:
Returns:

Returns a RethrowingAsyncErrorWrapper function that takes a function as argument, which is then wrapped by a try/catch and returns a NeverFailAsyncErrorWrappedFn function. The wrapped function returns the return value of the function it wraps or undefined in case of an error, unless rethrow is true.

Type: RethrowingAsyncErrorWrapper

(static) createArrayValueMap(mapObjopt) → {ArrayValueMap}

Parameters:
Name Type Attributes Description
mapObj Map.<*, Array.<*>> <optional>

An existing Map object can be provided, if not a new one is created

Convenience wrapper for "accumulator Map objects" where new entries are added per each key into an array to accumulate the entries for that key. Since this is only for conveniently adding entries the underlying Map object is fully exposed and only an "add" method is supplied to be used instead of the one on the Map object itself. It also provides a "map" function that creates a new Map object by applying a given callback function to each array value. This can be used to condense the array to a single value, for example.

Source:
Returns:

Returns an ArrayValueMap API-object

Type: ArrayValueMap

(static) flat(arr, depthopt) → {Array.<*>}

Parameters:
Name Type Attributes Default Description
arr Array.<*>

The array to be flattened remains unchanged

depth number <optional>
1

Flatten an array that contains values and arrays. Default behavior is to flatten a single level deep.

Source:
Returns:

Returns a new array that is the flattened version of the input array

Type: Array.<*>

(static) concatArrays(…arrays) → {Array.<T>}

Parameters:
Name Type Attributes Description
arrays Array.<T> | T <repeatable>

List of arrays and/or individual elements to be concatenated into one new array (in the given order)

Spreading into a new array using [...arr1, ...ar2] syntax still is unreliable. Example: https://github.com/Moddable-OpenSource/moddable/issues/140 In addition, unlike spread syntax this function allows individual values and undefined. The latter is filtered out when it is a direct argument, if it is a value in one of the given arrays it will be included in the result array.

Source:
Returns:

Returns a new array

Type: Array.<T>

(static) iterateArrayFromEnd(arr) → {IterableIterator}

Parameters:
Name Type Description
arr Array.<T>

The array to iterate over

Creates an Iterator object that lets one iterate over an array starting from the last element towards the first one.

Example:

const myArray = [1, 2, 3, 4, 5];

for (const item of iterateArrayFromEnd(myArray)) {
    // "item" is 5, then 4, 3, 2, 1
}

// 5
const lastElement = iterateArrayFromEnd(myArray).next().value;
Source:
Returns:

Returns an Iterator object

Type: IterableIterator

(static) memoize(fn, keyFuncopt) → {function}

Parameters:
Name Type Attributes Description
fn function

The function whose results are to be cached

keyFunc function <optional>

An optional function that receives the arguments as an array parameter to create a key for the cache. By default, the first argument to the fn function is used. Since the cache is a Map object types other than strings can be used, but if the type is an object remember that it must be the exact same object (memory reference) to get the cached result.

Takes a function and returns a wrapped function that caches the results of the given function.

Source:
Returns:

Returns a function that returns the cached result if it is available, otherwise it runs the supplied function

Type: function

(static) throttleWithFixedDelay(fn, delay, onErroropt) → {ThrottledFunction}

Parameters:
Name Type Attributes Description
fn AnyFunction

The function whose calls are to be throttled but with fixed delay execution guarantee. Its return values, if there are any, are lost. NOTE: If there is no onError function make sure the function does not throw or returns a rejected promise. The function must completely handle all its errors in that case.

delay number

The fixed "best effort" delay in milliseconds. It is only as accurate as a Javascript timer can be, so slight deviations are to be expected (also depends on how full the Javascript event loop is at the time).

onError function <optional>

Since the function will be run through setTimeout it should not throw any errors or return a rejected promise. If an onError callback function is provided errors will be caught and reported through this callback.

This is a throttle function with a "guaranteed and fixed delay since call" guarantee: Whenever the function gets called, its execution always takes place exactly delay milliseconds later. Any calls to the function between the first call and the execution are ignored. Any calls to the function after it executed again start the delayed execution. This means that there will always be an execution of the given function after any call to the throttled function, be it through the active timer or through setting up a new timer.

Unless you provide an onError callback the function should not throw any errors or return a promise that could be rejected. It is executed through setTimeout, i.e. it has no parent to catch the error.

Arguments: The throttled function does not take any arguments. The reason is that since calls to the function other than the ones that start the timer are discarded. Since it is unforeseeable which ones are discarded allowing the function to take arguments might lead to hard-to-debug errors in your code unless you design for that fact. Whatever the function needs should be provided from its environment, e.g. an array that accumulates data and is processed and emptied by the throttled function. The reference to the data should be bound or provided in the function's lexical scope before the throttled wrapper is created.

Source:
Returns:

Returns an object with the throttled function and a function to cancel the timer if it is running.

Type: ThrottledFunction