Various TS utils
npm install @rom98m/utilsshell
npm install --save @rom98m/utils
`
Usage
The utils support both ESM and CommonJS:
`ts
import { AsyncBatch } from "@rom98m/utils"
// --- or ---
// const { AssertBatch } = require("@rom98m/utils")
const batch = new AsyncBatch({ batchSize: 10 })
for (let i = 0; i <= 1e4; i++) {
batch.add(() => fetch(https://some.site/post?id=${i}))
}
batch
.run()
.then(res => / ... /)
.catch(err => / ... /)
`
Array utils
$3
Wraps given argument in array if needed.
`ts
/**
* @param {T | T[]} maybeArray Value (might be arra y or a single one).
* @returns {T[]}
*/
`
`ts
asArray(5) // => [5]
asArray([5]) // => [5] // left as is
`
$3
Returns the last element of given array.
`ts
/**
* @param {T[]} array Given array.
* @returns {T} Last element or undefined (in case of empty array).
*/
`
`ts
last([1, 2, 3]) // => 3
last("fdsa") // => "a"
`
$3
Split the array into partitions of given size.
`ts
/**
* @param {T[]} array
* @param {number} partitionSize
* @returns {T[][]}
*/
`
`ts
partition([1, 2, 3, 4, 5], 2) // => [[1, 2], [3, 4], [5]]
`
$3
Splits the array into 2 buckets:
- Left bucket contains all elements where the predicate() returned true;
- Right bucket contains all elements where the predicate() returned false.
`ts
/**
* @param {T[]} array Given array.
* @param {(item: T, index: number, arr: T[]) => boolean} predicate A method that is applied to each element.
* If returns true, the element appears in the left bucket,
* otherwise in the right one.
* @returns {[T[], T[]]}
*/
`
`ts
partitionBy([1, 2, 4, 5, 5], n => n % 2 === 0) // => [[2, 4], [1, 3, 5]]
`
Async utils
$3
A container for async tasks that should be run in batches.
For instance, there are 1000 requests to a remote server:
- running them sequentially is too slow;
- running them all at once can overwhelm/DoS the server.
But running (for instance) a sequence of 40 batches of 25 simultanious requests
might be a good balance:
`ts
parallel(25_requests)
.then(() => parallel(25_requests))
.then(() => parallel(25_requests))
// ...
.then(() => parallel(25_requests))
`
$3
#### new AsyncBatch({ batchSize = 10 })
Creates a new container for async tasks.
`ts
/**
* @param {object?} [param={}] Configuration of the task container.
* @param {number} [param.batchSize=10] The size of the batch
* (number of task to run simultaneously).
*/
`
`ts
const batch = new AsyncBatch({ batchSize: 5 })
const batch = new AsyncBatch()
`
$3
#### Status flags (readonly):
- .isRunning: boolean
- .isFinished: boolean
- .totalTasks: number returns the total number of tasks added
#### .results: AsyncResult
Readonly. \
Keeps the refetence to the array of results. \
Useful when results are referenced outside the .run().then(res => {/ ... /}).
> ⚠️ Watch out! \
> The array is filled dynamically upon task execution (during the run() routine). \
> Make sure to reference the .results _after_ the .run() is finished.
#### .add(...tasks: Array<() => Promise
Add given tasks to the container.
`ts
/**
* @param {...() => Promise} tasks Tasks to add.
* @returns {AsyncBatch} The instance of the AsyncBatch ( this) to make the method chainable.
* @throws When trying to add the task to a running or finished batch.
*/
`
`ts
batch
.add(() => fetch(/ ... /))
.add(() => fetch(/ ... /))
const tasks = postIds
.map(id => () => fetch(https://some.site/post?id=${id}))
// ^^^^^ the function is returned!
batch.add(...tasks)
`
#### run(): Promise
Run all tasks in batches.
`ts
/**
* @returns {Promise[][]>} Promise that resolves with all task results
* combined in sub-arrays (per batch).
* @throws When trying to run the batch for the 2nd time.
*/
`
`ts
batch
.run()
.then(res => {
res.flat() // because it's a 2D array of per-batch results
res.forEach(({ error, result, batch, task }) => {
console.info(Batch ${batch}, task ${task}: ${error ? "failed" : "succeeded"}:, error || result)
})
})
.catch(err => / ... /)
`
$3
`ts
/**
* A wrapper for the result of an async task.
*/
type AsyncResult = {
error: undefined | string | object
result: T
batch: number
task: number
}
`
Math/Random utils
$3
Returns random int from the interval [min(from, to) .. max(from, to)).
`ts
/**
* @param {number} from
* @param {number?} [to] Optional. If omitted consider random(0, from).
* @returns {number}
*/
`
`ts
random(-5, 5)
random(5, -5) // order does not matter
random(5) // equal to random(0, 5)
`
$3
Picks a random value from given array. Works against string as well, picking random character.
`ts
/**
* @param {T[] | string} arrayLike
* @returns {T | string}
*/
`
`ts
pickRandom([1, 2, 3, 4, 5]) // => some number
pickRandom("ACBDEF") // => some char
`
$3
Return true with given % of probability.
`ts
/**
* @param {number} probability Probability percentage (0..100).
* @returns {boolean}
*/
`
`ts
if (withProbability(25)) turn(withProbability(50) ? "left" : "right")
moveForward()
`
$3
Generates random string of given length.
`ts
/**
* @param {number} length
* @param {string?} chars Optional. Define the chars dictionary for generation.
* If omitted, lower English (a..z) letter are used.
* @returns {string}
*/
`
`ts
randomString(5)
randomString(10, dictionary.englishLower + dictionary.englishUpper + dictionary.numbers)
`
$3
`ts
export const dictionary = {
englishLower: "abcdefghijklmnopqrstuvwxyz",
englishUpper: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
numbers: "1234567890",
}
`
Time utils
$3
Returns a Promise which resolves after given time.
`ts
/**
* @param {number} ms Time (milliseconds).
* @returns {Promise}
*/
`
`ts
wait(100).then(() => console.log("After 100ms"))
`
$3
Ensures that given function is invoked not too often.
Think of eliminating "noisy" calls:
`
interval
: : : : : : :
--:------:------:------:------:------:------:--> time
: : : : : : :
A B C : D E F:G H I : : J : normal (noisy) function calls
: : : : : : :
A C F H I : J : debounced calls
: : : : : : :
`
`ts
/**
* @param {(...args: any[]) => void} fn (Repeatable) action to debounce.
* @param {number} intervalMs Minimal interval between consecutive calls.
* @returns {(...args: any[]) => void} Debounced function.
*/
`
`ts
const debouncedSend((input) => {
fetch({ url: "/api/...", method: "POST", data: { input } })
.then(res => / ... /)
.catch(err => / ... /)
}, 500)
`
Various/Unsorted utils
$3
Return an iterable that runs given number of times.
`ts
/**
* @param {number} n
* @returns {Generator & { do((val: number) => void): void }}
*/
`
`ts
for (const x of times(5)) {
// Do something
}
// Alternative syntax:
times(5).do(n => conosle.info(n))
``