UNPKG

@o3r/core

Version:
75 lines 3.91 kB
import { BehaviorSubject, EMPTY, from, identity, isObservable, merge, of, } from 'rxjs'; import { catchError, delay, filter, finalize, pairwise, startWith, switchMap, tap, } from 'rxjs/operators'; import { isIdentifiedCallAction, } from './async.helpers'; /** * Determine if the given parameter is a Promise * @param object */ const isPromise = (object) => object && typeof object === 'object' && typeof object.then !== 'undefined'; /** * Custom operator to use instead of SwitchMap with effects based on FromApi actions. * It makes sure to emit an action when the inner subscription is unsubscribed in order to keep the store up-to-date with pending information. * @param successHandler function that returns the action to emit in case the FromApi call is a success * @param errorHandler function that returns the action to emit in case the FromApi call fails * @param cancelRequestActionFactory function that returns the action to emit in case the FromApi action is 'cancelled' because a new action was received by the switchMap */ export function fromApiEffectSwitchMap(successHandler, errorHandler, cancelRequestActionFactory) { const pendingRequestIdsContext = {}; return (source$) => source$.pipe(tap((action) => { if (isIdentifiedCallAction(action)) { pendingRequestIdsContext[action.requestId] = true; } }), startWith(undefined), pairwise(), switchMap(([previousAction, action]) => { if (!action) { return EMPTY; } const isPreviousActionStillRunning = isIdentifiedCallAction(previousAction) && pendingRequestIdsContext[previousAction.requestId]; const cleanStack = () => { if (isIdentifiedCallAction(action)) { delete pendingRequestIdsContext[action.requestId]; } }; return from(action.call).pipe(tap(cleanStack), switchMap((result) => { const success = successHandler(result, action); return isObservable(success) ? success : (isPromise(success) ? success : of(success)); }), catchError((error) => { cleanStack(); return errorHandler?.(error, action) || EMPTY; }), isPreviousActionStillRunning && cancelRequestActionFactory ? startWith(cancelRequestActionFactory({ requestId: previousAction.requestId }, action)) : identity); })); } /** * Same as {@link fromApiEffectSwitchMap}, instead one inner subscription is kept by id. * @param successHandler * @param errorHandler * @param cancelRequestActionFactory * @param cleanUpTimer */ export function fromApiEffectSwitchMapById(successHandler, errorHandler, cancelRequestActionFactory, cleanUpTimer) { const innerSourcesById = {}; return (source$) => { return source$.pipe(filter((action) => { if (!isIdentifiedCallAction(action) || !action.id) { return false; } if (isIdentifiedCallAction(action) && innerSourcesById[action.id]) { innerSourcesById[action.id][0].next(action); return false; } return true; }), switchMap((action) => { const newIdSubject = new BehaviorSubject(action); const newId$ = newIdSubject.pipe(fromApiEffectSwitchMap(successHandler, errorHandler, cancelRequestActionFactory)); innerSourcesById[action.id] = [newIdSubject, newId$]; if (cleanUpTimer !== undefined) { newIdSubject.pipe(switchMap((myAction) => from(myAction.call).pipe(delay(cleanUpTimer), finalize(() => { delete innerSourcesById[myAction.id]; newIdSubject.complete(); })))).subscribe(); } const streams = Object.values(innerSourcesById).map(([_, obs]) => obs); return merge(...streams); })); }; } //# sourceMappingURL=async.operators.js.map