async-wrappers.js

import {wrapAsPromise, yielding} from './wrappers'
import {stringify} from './json'
import {parse} from './yastjson/lib/parse'
import {sort} from './timsort'
import {
    append,
    concat,
    every,
    filter,
    find,
    findIndex,
    forEach, groupBy,
    includes,
    indexOf, keyBy, lastIndexOf,
    map,
    reduce,
    some, uniqueBy,
} from './array-utilities'
import {LZStringGenerator} from './lz-string/lz-string'
import {Base64StringGenerator} from './lz-string/base64-string'
import {call, run} from './coroutines'

/**
 * Create a promised function
 * @param {Function} fn
 * @returns {Function}
 */
function wrapAsPromiseAndYieldFn(fn) {
    const result = function(array, processor, ...params) {
        return run(fn(array, yielding(processor), ...params))
    }
    result.with = function(...params) {
        return call(result, ...params)
    }
    return result
}

/**
 * @callback SortFunction
 * @param {any} item1
 * @param {any} item2
 * @returns < 0 if item 2 is bigger than item 1, === 0 if they are the same else > 0
 */

/**
 * @callback ExtractFunction
 * @param {any} item1
 * @returns {any} the value to sort item 1 by
 */

/**
 * Asynchronously stringify data into JSON
 * @function
 * @name stringifyAsync
 * @param {any} data - Object to store
 * @returns {Promise.<String>} a Promise for the JSON representation of <code>data</code>
 */
export const stringifyAsync = wrapAsPromise(stringify)
/**
 * Asynchronously parse JSON into an object
 * @function parseAsync
 * @param {String} json - the JSON to be parsed
 * @returns {Promise.<any>} a Promise for the parsed JSON
 */
export const parseAsync = wrapAsPromise(parse)
/**
 * <p>Sort an array (in place) by a sorting function</p>
 * <p>Sorts an array in place asynchronously. This function is a yielding
 * implementation of Timsort (standard sort used in modern browsers). Timsort
 * is fast and stable making it ideal for multi-key sorts. It it not as fast
 * as Quicksort.</p>
 * @function sortAsync
 * @param {Array} array - The array to sort
 * @param {SortFunction|ExtractFunction} sort - The method to sort the array
 * @returns {Promise.<Array>} a promise for the sorted array
 * @example
 * async function process(data) {
 *     return await sortAsync(data, v=>v.someProperty)
 * }
 */
export const sortAsync = wrapAsPromise(sort)
/**
 * Finds an item in an array asynchronously
 * @function findAsync
 * @param {Array} array
 * @param {Filter} filter
 * @returns {Promise.<any|null>} promise for the item found or null if no match
 */
export const findAsync = wrapAsPromiseAndYieldFn(find)
/**
 * Finds an item index in an array asynchronously
 * @function findIndexAsync
 * @param {Array} array
 * @param {Filter} filter
 * @returns {Promise.<Number>} promise for the index of the first item to pass the filter or -1
 */
export const findIndexAsync = wrapAsPromiseAndYieldFn(findIndex)
/**
 * Maps the contents of an array asynchronously
 * @function mapAsync
 * @param {Array} array
 * @param {Map} mapFn
 * @returns {Promise.<Array>} promise for the mapped array
 */
export const mapAsync = wrapAsPromiseAndYieldFn(map)
/**
 * Filters an array asynchronously
 * @function filterAsync
 * @param {Array} array
 * @param {Filter} filter
 * @returns {Promise.<Array>} promise for the filtered array
 */
export const filterAsync = wrapAsPromiseAndYieldFn(filter)
/**
 * Performs a reduce on an array asynchronously
 * @function reduceAsync
 * @param {Array} array
 * @param {Reduce} reduceFn
 * @param {any} initialValue
 * @returns {Promise.<any>} a promise for the reduced value
 */
export const reduceAsync = wrapAsPromiseAndYieldFn(reduce)
/**
 * Appends one array to another asynchronously
 * @function appendAsync
 * @param {Array} destination
 * @param {Array} source
 * @returns {Promise.<Array>} a promise for destination after appending
 */
export const appendAsync = wrapAsPromise(append)
/**
 * Concatenates 2 arrays into a new array
 * @function concatAsync
 * @param {Array} array1
 * @param {Array} array2
 * @returns {Promise.<Array>} a promise for combined array
 */
export const concatAsync = wrapAsPromise(concat)
/**
 * Asynchronously loop over the elements of an array
 * @function forEachAsync
 * @param {Array} array
 * @param {Process} fn
 * @returns {Promise} promise for the end of the operation
 */
export const forEachAsync = wrapAsPromiseAndYieldFn(forEach)
/**
 * Asynchronously apply an array <code>some</code> operation
 * returning a promise for <code>true</code> if at least
 * one item matches
 * @function someAsync
 * @param {Array} array
 * @param {Filter} fn
 * @returns {Promise.<Boolean>} promise for true if at least one item matched the filter
 */
export const someAsync = wrapAsPromiseAndYieldFn(some)
/**
 * Asynchronously check if every element in an array matches
 * a predicate
 * @function everyAsync
 * @param {Array} array
 * @param {Filter} fn
 * @returns {Promise.<Boolean>} promise for true if all items matched the filter
 */
export const everyAsync = wrapAsPromiseAndYieldFn(every)
/**
 * Asynchronously compress a string to a base64 format
 * @function compressToBase64Async
 * @param {String} source - the data to compress
 * @returns {Promise.<String>} a promise for the base64 compressed data
 */
export const compressToBase64Async = wrapAsPromise(
    LZStringGenerator.compressToBase64
)


/**
 * Asynchronously compress a string to a utf16 string
 * @function compressToUTF16Async
 * @param {String} source - the data to compress
 * @returns {Promise.<String>} a promise for the utf16 compressed data
 */
export const compressToUTF16Async = wrapAsPromise(LZStringGenerator.compressToUTF16)
/**
 * Asynchronously compress a string to a Uint8Array
 * @function compressToUint8ArrayAsync
 * @param {String} source - the data to compress
 * @returns {Promise.<Uint8Array>} a promise for the Uint8Array of compressed data
 */
export const compressToUint8ArrayAsync = wrapAsPromise(
    LZStringGenerator.compressToUint8Array
)
/**
 * Asynchronously compress a string to a URI safe version
 * @function compressToEncodedURIComponentAsync
 * @param {String} source - the data to compress
 * @returns {Promise.<String>} a promise for the string of compressed data
 */
export const compressToEncodedURIComponentAsync = wrapAsPromise(
    LZStringGenerator.compressToEncodedURIComponent
)
/**
 * Asynchronously compress a string of data with lz-string
 * @function compressAsync
 * @param {String} source - the data to compress
 * @returns {Promise.<String>} a promise for the compressed data
 */
export const compressAsync = wrapAsPromise(LZStringGenerator.compress)
/**
 * Asynchronously apply lz-string base64 remapping of a string to utf16
 * @function base64CompressToUTF16Async
 * @param {String} source - the data to compress
 * @returns {Promise.<String>} a promise for the compressed data
 */
export const base64CompressToUTF16Async = wrapAsPromise(
    Base64StringGenerator.compressToUTF16
)
/**
 * Asynchronously apply lz-string base64 remapping of a string
 * @function base64CompressAsync
 * @param {String} source - the data to compress
 * @returns {Promise.<String>} a promise for the compressed data
 */
export const base64Compress = wrapAsPromise(Base64StringGenerator.compress)
/**
 * Asynchronously decompress a string from a base64 source
 * @function compressToBase64Async
 * @param {String} compressedData - the data to decompress
 * @returns {Promise.<String>} a promise for the uncompressed data
 */
export const decompressFromBase64Async = wrapAsPromise(
    LZStringGenerator.decompressFromBase64
)
/**
 * Asynchronously decompress a string from a utf16 source
 * @function decompressFromUTF16Async
 * @param {String} compressedData - the data to decompress
 * @returns {Promise.<String>} a promise for the uncompressed data
 */
export const decompressFromUTF16Async = wrapAsPromise(
    LZStringGenerator.decompressFromUTF16
)
/**
 * Asynchronously decompress a string from a utf16 source
 * @function decompressFromUint8ArrayAsync
 * @param {String} compressedData - the data to decompress
 * @returns {Promise.<String>} a promise for the uncompressed data
 */
export const decompressFromUint8ArrayAsync = wrapAsPromise(
    LZStringGenerator.decompressFromUint8Array
)
/**
 * Asynchronously decompress a string from a URL safe URI Component encoded source
 * @function decompressFromEncodedURIComponentAsync
 * @param {String} compressedData - the data to decompress
 * @returns {Promise.<String>} a promise for the uncompressed data
 */
export const decompressFromEncodedURIComponentAsync = wrapAsPromise(
    LZStringGenerator.decompressFromURIComponent
)
/**
 * Asynchronously decompress a string from a string source
 * @function decompressAsync
 * @param {String} compressedData - the data to decompress
 * @returns {Promise.<String>} a promise for the uncompressed data
 */
export const decompressAsync = wrapAsPromise(LZStringGenerator.decompress)
/**
 * Asynchronously unmap base64 encoded data to a utf16 destination
 * @function base64decompressFromUTF16Async
 * @param {String} base64Data - the data to decompress
 * @returns {Promise.<String>} a promise for the uncompressed data
 */
export const base64decompressFromUTF16Async = wrapAsPromise(
    Base64StringGenerator.decompressFromUTF16
)
/**
 * Asynchronously unmap base64 encoded data
 * @function base64Decompress
 * @param {String} base64Data - the data to decompress
 * @returns {Promise.<String>} a promise for the uncompressed data
 */
export const base64Decompress = wrapAsPromise(Base64StringGenerator.decompress)


/**
 * Returns a promise returning true if an array includes a value
 * @param array
 * @param value
 * @returns Promise<Boolean>
 * @example
 * if(await includesAsync(someArray, 'error')) {
 *     ...
 * }
 */
export const includesAsync = wrapAsPromiseAndYieldFn(includes)

/**
 * Returns a promise for the first index of an item in an array
 * @param array - the array to scan
 * @param value - the value to search for
 * @returns {Promise<Number>}
 */
export const indexOfAsync = wrapAsPromiseAndYieldFn(indexOf)


/**
 * Returns a promise for the last index of an item in an array
 * @param array - the array to scan
 * @param value - the value to search for
 * @returns {Promise<Number>}
 */
export const lastIndexOfAsync = wrapAsPromiseAndYieldFn(lastIndexOf)


/**
 * Promises the creation an object composed of keys generated from the results
 * of running each element of collection thru then supplied function.
 * The corresponding value of each key is the last element responsible
 * for generating the key.
 *
 * @param {Array|Object} collection
 * @param {Map} fn
 * @returns {Promise<{}>}
 */
export const keyByAsync = wrapAsPromiseAndYieldFn(keyBy)


/**
 * Promises the creation of an object composed of keys generated from the results
 * of running each element of collection thru then supplied function.
 * The corresponding value of each key is an collection of the elements responsible
 * for generating the key.
 *
 * @param {Array|Object} collection
 * @param {Map} fn
 * @returns {Promise<{}>}
 */
export const groupByAsync = wrapAsPromiseAndYieldFn(groupBy)

/**
 * Promises the creation of an array with the unique values from the
 * input array, the routine is supplied with a
 * function that determines on what the array should
 * be made unique.
 * @param {Array} array
 * @param {Map} [fn] - the function to determine uniqueness, if
 * omitted then the item itself is used
 * @returns {Promise<Array>} unique array
 */
export const uniqueByAsync = wrapAsPromiseAndYieldFn(uniqueBy)