@naturalcycles/js-lib
Version:
Standard library for universal (browser + Node.js) javascript
80 lines (79 loc) • 2.56 kB
JavaScript
import { _isTruthy } from './is.util.js';
/**
* Creates AbortableSignal,
* which is like AbortSignal, but can "abort itself" with `.abort()` method.
*
* @experimental
*/
export function createAbortableSignal() {
const ac = new AbortController();
return Object.assign(ac.signal, {
abort: ac.abort.bind(ac),
});
}
/**
* Returns AbortSignal if ms is defined.
* Otherwise returns undefined.
*/
export function abortSignalTimeoutOrUndefined(ms) {
return ms ? abortSignalTimeout(ms) : undefined;
}
/**
* Returns an AbortSignal that aborts after the given number of milliseconds.
* Uses native `AbortSignal.timeout()` when available, falls back to a polyfill.
*
* The abort reason is a DOMException with name "TimeoutError".
*/
export function abortSignalTimeout(ms) {
return typeof AbortSignal.timeout === 'function'
? AbortSignal.timeout(ms)
: polyfilledAbortSignalTimeout(ms);
}
export function polyfilledAbortSignalTimeout(ms) {
const ac = new AbortController();
setTimeout(() => {
ac.abort(new DOMException('The operation was aborted due to timeout', 'TimeoutError'));
}, ms);
return ac.signal;
}
/**
* Returns AbortSignal.any(signals) is the array (after filtering undefined inputs) is not empty,
* otherwise undefined.
*/
export function abortSignalAnyOrUndefined(signals) {
const filtered = signals.filter(_isTruthy);
return filtered.length ? abortSignalAny(filtered) : undefined;
}
/**
* Returns an AbortSignal that aborts when any of the given signals abort.
* Uses native `AbortSignal.any()` when available, falls back to a polyfill.
*
* The abort reason is taken from the first signal that aborts.
* If any input signal is already aborted, the returned signal is immediately aborted.
*
* If only 1 signal is passed in the input array - that Signal is returned as-is.
*/
export function abortSignalAny(signals) {
if (signals.length === 1) {
return signals[0];
}
return typeof AbortSignal.any === 'function'
? AbortSignal.any(signals)
: polyfilledAbortSignalAny(signals);
}
export function polyfilledAbortSignalAny(signals) {
const ac = new AbortController();
for (const signal of signals) {
if (signal.aborted) {
ac.abort(signal.reason);
return ac.signal;
}
}
for (const signal of signals) {
signal.addEventListener('abort', () => ac.abort(signal.reason), {
once: true,
signal: ac.signal,
});
}
return ac.signal;
}