UNPKG

@whatwg-node/promise-helpers

Version:
198 lines (197 loc) 5.83 kB
export function isPromise(value) { return value?.then != null; } export function handleMaybePromise(inputFactory, outputSuccessFactory, outputErrorFactory) { function _handleMaybePromise() { const input$ = inputFactory(); if (isPromise(input$)) { return input$.then(outputSuccessFactory, outputErrorFactory); } return outputSuccessFactory(input$); } if (!outputErrorFactory) { return _handleMaybePromise(); } try { return _handleMaybePromise(); } catch (err) { return outputErrorFactory(err); } } export function fakePromise(value) { if (isPromise(value)) { return value; } // Write a fake promise to avoid the promise constructor // being called with `new Promise` in the browser. return { then(resolve) { if (resolve) { const callbackResult = resolve(value); if (isPromise(callbackResult)) { return callbackResult; } return fakePromise(callbackResult); } return this; }, catch() { return this; }, finally(cb) { if (cb) { const callbackResult = cb(); if (isPromise(callbackResult)) { return callbackResult.then(() => value, () => value); } return fakePromise(value); } return this; }, [Symbol.toStringTag]: 'Promise', }; } export function createDeferredPromise() { if (Promise.withResolvers) { return Promise.withResolvers(); } let resolveFn; let rejectFn; const promise = new Promise(function deferredPromiseExecutor(resolve, reject) { resolveFn = resolve; rejectFn = reject; }); return { promise, get resolve() { return resolveFn; }, get reject() { return rejectFn; }, }; } export { iterateAsync as iterateAsyncVoid }; export function iterateAsync(iterable, callback, results) { if (iterable?.length === 0) { return; } const iterator = iterable[Symbol.iterator](); function iterate() { const { done: endOfIterator, value } = iterator.next(); if (endOfIterator) { return; } let endedEarly = false; function endEarly() { endedEarly = true; } return handleMaybePromise(() => callback(value, endEarly), result => { if (endedEarly) { return; } if (result) { results?.push(result); } return iterate(); }); } return iterate(); } export function fakeRejectPromise(error) { if (isPromise(error)) { return error; } return { then() { return this; }, catch(reject) { if (reject) { return fakePromise(reject(error)); } return this; }, finally(cb) { if (cb) { cb(); } return this; }, [Symbol.toStringTag]: 'Promise', }; } export function mapMaybePromise(input, onSuccess, onError) { return handleMaybePromise(() => input, onSuccess, onError); } /** * Given an AsyncIterable and a callback function, return an AsyncIterator * which produces values mapped via calling the callback function. */ export function mapAsyncIterator(iterator, onNext, onError, onEnd) { if (Symbol.asyncIterator in iterator) { iterator = iterator[Symbol.asyncIterator](); } let $return; let abruptClose; let onEndWithValue; if (onEnd) { let onEndWithValueResult /** R in onEndWithValue */; onEndWithValue = value => { onEndWithValueResult ||= handleMaybePromise(onEnd, () => value, () => value); return onEndWithValueResult; }; } if (typeof iterator.return === 'function') { $return = iterator.return; abruptClose = (error) => { const rethrow = () => { throw error; }; return $return.call(iterator).then(rethrow, rethrow); }; } function mapResult(result) { if (result.done) { return onEndWithValue ? onEndWithValue(result) : result; } return handleMaybePromise(() => result.value, value => handleMaybePromise(() => onNext(value), iteratorResult, abruptClose)); } let mapReject; if (onError) { let onErrorResult; // Capture rejectCallback to ensure it cannot be null. const reject = onError; mapReject = (error) => { onErrorResult ||= handleMaybePromise(() => error, error => handleMaybePromise(() => reject(error), iteratorResult, abruptClose)); return onErrorResult; }; } return { next() { return iterator.next().then(mapResult, mapReject); }, return() { const res$ = $return ? $return.call(iterator).then(mapResult, mapReject) : fakePromise({ value: undefined, done: true }); return onEndWithValue ? res$.then(onEndWithValue) : res$; }, throw(error) { if (typeof iterator.throw === 'function') { return iterator.throw(error).then(mapResult, mapReject); } if (abruptClose) { return abruptClose(error); } return fakeRejectPromise(error); }, [Symbol.asyncIterator]() { return this; }, }; } function iteratorResult(value) { return { value, done: false }; }