Utility for JavaScript Promise and AsyncFunction.
npm install --save extra-promise
# or
yarn add extra-promise
function isPromise<T>(val: any): val is Promise<T>
Check if the val
is a Promise
instance.
function isPromiseLike<T>(val: any): val is PromiseLike<T>
Check if the val
has a then
method.
function delay(timeout: number): Promise<void>
A simple wrapper for setTimeout
.
function timeout(ms: number): Promise<never>
It throws a TimeoutError
after ms
milliseconds.
try {
result = await Promise.race([
fetchData()
, timeout(5000)
])
} catch (e) {
if (e instanceof TimeoutError) ...
}
function retryUntil<T, U = unknown>(fn: () => T | PromiseLike<T>, until: (error: U) => boolean | PromiseLike<boolean>): Promise<T>
If fn
function throws an error, continue to retry until the return value of the until
function is true.
function parallel<T>(tasks: Iterable<() => T | PromiseLike<T>>, concurrency: number = Infinity): Promise<void>
Perform tasks in parallel.
The value range of concurrency
is [1, Infinity]. Invalid values will throw InvalidArugmentError
.
function series<T>(tasks: Iterable<() => T | PromiseLike<T>>): Promise<void>
Perform tasks in order. Equivalent to parallel(tasks, 1)
.
function waterfall<T>(tasks: Iterable<(result: unknown) => unknown | PromiseLike<unknown>>): Promise<T | undefined>
Perform tasks in order, the return value of the previous task will become the parameter of the next task. If tasks
is empty, return Promise<undefined>
.
function each(iterable: Iterable<T>, fn: (element: T, i: number) => unknown | PromiseLike<unknown>, concurrency: number = Infinity): Promise<void>
The async each
operator for Iterable.
The value range of concurrency
is [1, Infinity]. Invalid values will throw InvalidArugmentError
.
function map<T, U>(iterable: Iterable<T>, fn: (element: T, i: number) => U | PromiseLike<U>, concurrency: number = Infinity): Promise<U[]>
The async map
operator for Iterable.
The value range of concurrency
is [1, Infinity]. Invalid values will throw InvalidArugmentError
.
function filter<T, U = T>(iterable: Iterable<T>, fn: (element: T, i: number) => boolean | PromiseLike<boolean>, concurrency: number = Infinity): Promise<U[]>
The async filter
operator for Iterable.
The value range of concurrency
is [1, Infinity]. Invalid values will throw InvalidArugmentError
.
function promisify<Result, Args extends any[] = unknown[]>(fn: (...args: any[]) => unknown): (...args: Args) => Promise<Result>
The well-known promisify
function.
function callbackify<Result, Args extends any[] = unknown[]>(fn: (...args: Args) => PromiseLike<Result>): (...args: Args) => void
The callbackify
function, as opposed to promisify
.
The return value:
args.length = 0
then throw InvalidArgumentsLengthError
.InvalidArugmentError
.FalsyError
, the reason
property is the real error.function asyncify<T extends any[], U>(fn: (...args: T) => U | PromiseLike<U>): (...args: Promisify<T>) => Promise<U>
Turn sync functions into async functions.
const a = 1
const b = Promise.resolve(2)
const add = (a: number, b: number) => a + b
// BAD
add(a, await b) // 3
// GOOD
const addAsync = asyncify(add) // (a: number | PromiseLike<number>, b: number | PromiseLike<number>) => Promise<number>
await addAsync(a, b) // Promise<3>
function cascadify<T extends object>(target: T): Cascadify<T>
Use the decorator cascadable
to mark the cascadable methods (the return value is PromiseLike<this>
), transform the instance into a cascadify instance, and end with the non-cascadable member.
class Adder {
value: number
constructor(initialValue: number) {
this.value = initialValue
}
get() {
return this.value
}
async getAsync() {
return this.value
}
@cascadable
async add(value: number) {
this.value += value
return this
}
}
await cascadify(adder)
.add(10)
.get()
await cascadify(adder)
.add(10)
.getAsync()
await cascadify(adder)
.add(10)
.value
function makeChannel(): [(value: T) => void, () => AsyncIterable<T>, () => void]
function makeBlockingChannel(bufferSize: number): [(value: T) => Promise<void>, () => AsyncIterable<T>, () => void]
Implement “multi-producer, single-consumer” FIFO queue communication with Promise
and AsyncIterable
.
makeChannel
return a tuple includes three channel functions:
function send(value: T): void
function receive(): AsyncIterable<T>
function close(): void
makeBlockingChannel
is the asynchronous blocking version of makeChannel
implemented with Promise
. When the amount of data sent exceeds bufferSize
, blocking will occur until data is taken out by the consumer.
makeBlockingChannel
return a tuple includes three channel functions:
function send(value: T): Promise<void>
function receive(): AsyncIterable<T>
function close(): void
If the channel closed, send()
will throw ChannelClosedError
.
// makeChannel
const [send, receive, close] = makeChannel<string>()
queueMicrotask(() => {
send('hello')
send('world')
close()
})
for await (const value of receive()) {
console.log(value)
}
// makeBlockingChannel
const [send, receive, close] = makeBlockingChannel<string>(1)
queueMicrotask(() => {
await send('hello')
await send('world')
close()
})
for await (const value of receive()) {
console.log(value)
}
class Deferred<T> implements PromiseLike<T> {
then: PromiseLike<T>['then']
resolve(value: T): void
reject(reason: unknown): void
}
Deferred
is a Promise
that separates resolve()
and reject()
from the constructor.
class LazyPromise<T> implements PromiseLike<T> {
then: PromiseLike<T>['then']
constructor(executor: (resolve: (value: T) => void, reject: (reason: any) => void) => void)
}
LazyPromise
constructor is the same as Promise
.
The difference with Promise
is that LazyPromise
only performs executor
after then
method is called.
class Signal implements PromiseLike<void> {
then: PromiseLike<void>['then']
emit(): void
discard(): void
refresh(): void
}
The emit()
make the internal Promise resolve.
The discard()
make the internal Promise reject SignalDiscarded
.
The refresh()
re-creates the internal Promise.
Author: BlackGlory
Source Code: https://github.com/BlackGlory/extra-promise
#javascript