RxJs: How to only maintain the latest value until inner observable complete - javascript

I'm new to RxJs and having trouble to achieve this in "RxJs way":
An infinite stream a$ emits a value a once a while.
async() takes the a and performs an async operation.
If a$ emits values while async is pending, only keep the latest one al.
After the previous async completes, if there is an al, run async(al).
And so on.
a$:----a1----------a2----a3-----------------------a4-----------
async(a1):------------end async(a4):---
async(a3):-----end
Here is what I came up with, a bit nasty:
var asyncIdle$ = new Rx.BehaviorSubject()
var asyncRunning$ = new Rx.Subject()
var async$ = asyncIdle$
function async (val) {
async$ = asyncRunning$
// do something with val
console.log(val + ' handling')
setTimeout(() => {
console.log(val + ' complete')
async$.next()
async$ = asyncIdle$
}, 2000)
}
// simulate a$
var a$ = Rx.Observable.fromEvent(document, 'click')
.mapTo(1)
.scan((acc, curr) => acc + curr)
.do(val => console.log('got ' + val))
a$.debounce(() => async$)
.subscribe(val => {
async(val)
})

I came up with this solution in typescript:
I have a simple Gate class that can be open or closed:
enum GateStatus {
open = "open",
closed = "closed"
}
class Gate {
private readonly gate$: BehaviorSubject<GateStatus>;
readonly open$: Observable<GateStatus>;
readonly closed$: Observable<GateStatus>;
constructor(initialState = GateStatus.open) {
this.gate$ = new BehaviorSubject<GateStatus>(initialState);
this.open$ = this.gate$
.asObservable()
.pipe(filter(status => status === GateStatus.open));
this.closed$ = this.gate$
.asObservable()
.pipe(filter(status => status === GateStatus.closed));
}
open() {
this.gate$.next(GateStatus.open);
}
close() {
this.gate$.next(GateStatus.closed);
}
}
The operator function is quite simple. At the beginning the gate is open. Before we start a request, we close it and when the request has finished we open it again.
The audit() will only let the most recent request-data pass when the gate is open.
export const requestThrottle = <T>(
requestHandlerFactory: (requestData: T) => Observable<any>
) => (requestData: Observable<T>) => {
const gate = new Gate();
return requestData.pipe(
audit(_ => gate.open$),
// NOTE: when the order is important, use concatMap() instead of mergeMap()
mergeMap(value => {
gate.close();
return requestHandlerFactory(value).pipe(finalize(() => gate.open()));
})
);
};
use it like this:
src.pipe(
requestThrottle(() => of(1).pipe(delay(100)))
);
code exmaple on stackblitz

You can use the audit operator to solve the problem, like this (the comments should explain how it works):
// Simulate the source.
const source = Rx.Observable.merge(
Rx.Observable.of(1).delay(0),
Rx.Observable.of(2).delay(10),
Rx.Observable.of(3).delay(20),
Rx.Observable.of(4).delay(150),
Rx.Observable.of(5).delay(300)
).do(value => console.log("source", value));
// Simulate the async task.
function asyncTask(value) {
return Rx.Observable
.of(value)
.do(value => console.log(" before async", value))
.delay(100)
.do(value => console.log(" after async", value));
}
// Compose an observable that's based on the source.
// Use audit to ensure a value is not emitted until
// the async task has been performed.
// Use share so that the signal does not effect a
// second subscription to the source.
let signal;
const audited = source
.audit(() => signal)
.mergeMap(value => asyncTask(value))
.share();
// Compose a signal from the audited observable to
// which the async task is applied.
// Use startWith so that the first emitted value
// passes the audit.
signal = audited
.mapTo(true)
.startWith(true);
audited.subscribe(value => console.log("output", value));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://unpkg.com/rxjs#5/bundles/Rx.min.js"></script>

A Solution With RxJS v6+ Primatives
Here's a solution that works in current RxJS versions (now available as the Node package rxjs-audit-with):
export function auditWith<T, R>(
callback: (value: T) => Promise<any>,
): OperatorFunction<T, R> {
const freeToRun = new BehaviorSubject(true);
return (source: Observable<T>) => {
return source.pipe(
audit(_val => freeToRun.pipe(filter(free => free))),
tap(() => freeToRun.next(false)),
mergeMap(val => callback(val)),
tap(() => freeToRun.next(true)),
);
};
}
How It Works
auditWith is an operator that uses a BehaviorSubject to track whether callback is busy. It uses audit to keep only the most recent value to come down the pipe, and releases that value the next time the BehaviorSubject is marked as free. tap goes before and after the async call in order to make it as busy/free (respectively), and mergeMap is used to actually call the async callback. Note that callback receives the latest value from the pipe, but can return anything it wants (if anything at all); the same value will propagate down the pipe unchanged (by design).
An Example of Use
async function slowFunc(num: number) {
await new Promise(resolve =>
setTimeout(() => {
console.log("Processed num", num);
resolve(true);
}, 3000),
);
}
interval(500)
.pipe(
take(13),
auditWith(slowFunc),
)
.subscribe();
// Sample output:
// Processed num 0
// Processed num 5
// Processed num 11
// Processed num 12
Even though sequential numbers are generated by interval every 500ms, auditWith will hold the latest value in the pipe until slowFunc has returned from its previous iteration. Note that the very first value is guaranteed to be run (because the async function is assumed to start in the "available" state), and the last value in the Observable is also guaranteed to run (i.e. the pipe will drain).

use a combination of first() and repeat(). if a$ finished emission the sequence complete
//emit every 1s
const a$=new Rx.BehaviorSubject(0)
Rx.Observable.interval(1000).take(100).skip(1).subscribe(a$);
// //simulate aysnc
const async = (val)=>{
console.log('async start with:'+ val)
return Rx.Observable.timer(5100).mapTo('async done:'+val);
}
a$.first().switchMap(value=>async(value))
.repeat()
.catch(e=>Rx.Observable.empty())
.subscribe(console.log,console.err,console.warn)
a$.subscribe(console.warn)
https://jsbin.com/tohahod/65/edit?js,console

Related

RxJS 5, Angular: How to do a request every x seconds UNTIL a message matches a certain string?

New to Web-dev and Angular.
Here is my current code. Right now, all it does is calling a http requests every 2seconds and console log the message.
What I want is if the message matches to a string "5", let it unsubscribe. Also, how do i make this as non-nested subscribe? (is nested subcribe a bad practice?). Currently, i'm using Angular 5 and rxjs 5.
public checkProgress() {
Observable
.interval(2000)
.subscribe(
x => {
this.myService.getStatus()
.subscribe( data => console.log(data));
}
);
}
Also, if a user navigate to a different component, how do I unsubscribe it? ngOnDestroy?
There are multiple ways of doing it. Take your pick:
Recursion - one liner with ternary operator
expand(val => val === "5" ? this.myService.getStatus().pipe(delay(2000)) : empty())
Recursion with takeWhile
expand(() => this.myService.getStatus().pipe(delay(2000)))
.pipe(takeWhile(val => val !== "5"))
Interval with takeWhile
timer(0, 2000)
.pipe(
switchMap(() => this.myService.getStatus()),
takeWhile(val => val !== "5")
)
Use a subject to terminate the subscription with takeUntil
const { of, timer, Subject } = rxjs;
const { switchMap, takeUntil } = rxjs.operators;
const finalise$ = new Subject();
let finalised = false;
const finalise = () => {
if (!finalised) {
finalised = true;
finalise$.next();
finalise$.complete();
}
};
const timer$ = timer(0, 2000); // Polling timer
const api$ = () => of(Math.random() * 5); // Simulate an api, just a random number
timer$.pipe(
switchMap(_ => api$()),
takeUntil(finalise$)
).subscribe(apiData => {
console.log(apiData);
if (apiData < 1) { // Condition to termitate
finalise();
console.log('finalised');
}
});
// You can then call finalise in you OnDestroy method to kill the subscription
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.3.3/rxjs.umd.min.js"></script>

Toggling API call by a stream

Here:
import Rx from 'rxjs';
function fakeApi(name, delay, response) {
return new Rx.Observable(observer => {
console.log(`${name}: Request.`)
let running = true;
const id = setTimeout(() => {
console.log(`${name}: Response.`)
running = false;
observer.next(response);
observer.complete();
}, delay);
return () => {
if(running) console.log(`${name}: Cancel.`)
clearTimeout(id);
}
})
}
function apiSearch() { return fakeApi('Search', 4000, "This is a result of the search."); }
//============================================================
const messages$ = new Rx.Subject();
const toggle$ = messages$.filter(m => m === 'toggle');
const searchDone$ = toggle$.flatMap(() =>
apiSearch().takeUntil(toggle$)
);
searchDone$.subscribe(m => console.log('Subscriber:', m))
setTimeout(() => {
// This one starts the API call.
toggle$.next('toggle');
}, 2000)
setTimeout(() => {
// This one should only cancel the API call in progress, not to start a new one.
toggle$.next('toggle');
}, 3000)
setTimeout(() => {
// And this should start a new request again...
toggle$.next('toggle');
}, 9000)
my intent is to start the API call and stop it when it is in progress by the same toggle$ signal. Problem with the code is that toggle$ starts a new API call every time. I would like it not to start the new call when there is one already running, just to stop the one which is already in progress. Some way should I "unsubscribe" the outermost flatMap from toggle$ stream while apiSearch() is running. I guess that there is a need to restructure the code to achieve the behaviour... What is the RxJS way of doing that?
UPDATE: After some more investigations and user guide lookups, I came with this:
const searchDone$ = toggle$.take(1).flatMap(() =>
apiSearch().takeUntil(toggle$)
).repeat()
Works like it should. Still feels cryptic a little bit. Is this how you RxJS guys would solve it?
I think your solution will work only once since you're using take(1). You could do it like this:
const searchDone$ = toggle$
.let(observable => {
let pending;
return observable
.switchMap(() => {
let innerObs;
if (pending) {
innerObs = Observable.empty();
} else {
pending = innerObs = apiSearch();
}
return innerObs.finally(() => pending = null);
});
});
I'm using let() only to wrap pending without declaring it in parent scope. The switchMap() operator unsubscribes for you automatically without using take*().
The output with your test setTimeouts will be as follows:
Search: Request.
Search: Cancel.
Search: Request.
Search: Response.
Subscriber: This is a result of the search.

How to convert a sequence of Promises into Rx.Observable with RxJS?

I'm trying to create an Rx.Observable from a chain of Promises with RxJS. The difference from this question is that I have unknown number of Promises, and every Promise depends on the result of the previous one.
Basically I have a sequence of pages, connected with "next page" links.
What I want the function to do is:
Wait for Promise<>
Provide the result (fire observer.onNext())
Check if there is a next page link
Create next Promise<> with that link
Repeat until there are pages remained
I tried the following:
private getPages<T>(firstPromise: PromiseLike<IODataCollectionResult<T>>): Rx.Observable<T> {
let observable = Rx.Observable.create<T>(async obs => {
let page = await firstPromise;
page.value.forEach(v => obs.onNext(v));
while (page['#odata.nextLink']) {
let nextPageUrl = <string>page['#odata.nextLink'];
let nextPagePromise = <PromiseLike<IODataCollectionResult<T>>>this.resource(nextPageUrl).get().$promise;
page = await nextPagePromise;
page.value.forEach(v => obs.onNext(v));
}
obs.onCompleted();
});
return observable;
}
(IODataCollectionResult is a OData result, where '#odata.nextLink' is the next page url and .value is an array of values)
The problem is I can't compile that with TypeScript, it gives me an error:
Argument of type '(obs: Observer) => Promise' is not assignable to parameter of type '(observer: Observer) => void | Function | IDisposable'.
Which makes sense, because async function returns a Promise<void>, not a void.
Does it mean I cannot use async/await with the Rx.Observable.create()? How can I chain a sequence of Promises into an Observable?
You can wrap the async function in something that voids its results:
function toVoid<A>(fn: A => Any): A => Void {
return x => void fn(x)
}
(forgive my lacking knowledge of TypeScript, but I hope you can guess what it's supposed to do)
With that, you should be able to call
let observable = Rx.Observable.create<T>(toVoid(async obs => {
…
}));
But maybe you shouldn't do that. Don't throw away the promise, use it instead to attach the appropriate error handler:
let observable = Rx.Observable.create<T>(obs => {
(async () => {
…
}()).catch(err => {
obs.onError(err);
});
});
The problem was solved using .then() + recursion, without async/await:
private getPages<T>(initialPromise: PromiseLike<IODataCollectionResult<T>>): Rx.Observable<T> {
return Rx.Observable.create<T>(obs => {
const getPage = (promise: PromiseLike<IODataCollectionResult<T>>) => {
promise.then(page => {
page.value.forEach(v => obs.onNext(v));
if (page['#odata.nextLink']) {
let nextPageUrl = <string>page['#odata.nextLink'];
let nextPagePromise = <PromiseLike<IODataCollectionResult<T>>>this.resource(nextPageUrl).get().$promise;
getPage(nextPagePromise);
}
else {
obs.onCompleted();
}
});
}
getPage(initialPromise);
});
}

Convert infinite async callback sequence to Observable sequence?

Let's say I have the following asynchronous callback-based "infinite" sequence, which I cancel after some time:
'use strict';
const timers = require('timers');
let cancelled = false;
function asyncOperation(callback) {
const delayMsec = Math.floor(Math.random() * 10000) + 1;
console.log(`Taking ${delayMsec}msec to process...`);
timers.setTimeout(callback, delayMsec, null, delayMsec);
}
function cancellableSequence(callback) {
asyncOperation((error, processTime) => {
console.log('Did stuff');
if (!cancelled) {
process.nextTick(() => { cancellableSequence(callback); });
} else {
callback(null, processTime);
}
});
}
cancellableSequence((error, lastProcessTime) => {
console.log('Cancelled');
});
timers.setTimeout(() => { cancelled = true; }, 0);
The asyncOperation will execute and call back at least once, and the cancellation message will not display immediately, but rather after asyncOperation is complete. The number of calls to asyncOperation depends on the internal delayMsec value and the delay argument passed to setTimeout() at the end (an attempt to show that these are variable).
I'm starting to learn RxJS5, and thought it might be possible to convert this into an Observable sequence ("oooh, an Observable subscription can be unsubscribe()d - that looks neat!").
However, my attempts at turning cancellableSequence into an ES6 generator (how else to make infinite?) yielding Observable.bindNodeCallback(asyncOperation)() resulted in immediate yields, which in my case is undesired behavior.
I cannot use Observable.delay() or Observable.timer(), as I do not have a known, consistent interval. (The Math.random(...) in asyncOperation was an attempt to indicate that I as the caller do not control the timing, and the callback happens "some unknown time later.")
My failed attempt:
'use strict';
const timers = require('timers');
const Rx = require('rxjs/Rx');
function asyncOperation(callback) {
const delayMsec = Math.floor(Math.random() * 10000) + 1;
console.log(`Taking ${delayMsec}msec to process...`);
timers.setTimeout(callback, delayMsec, null, delayMsec);
}
const operationAsObservable = Rx.Observable.bindNodeCallback(asyncOperation);
function* generator() {
while (true) {
console.log('Yielding...');
yield operationAsObservable();
}
}
Rx.Observable.from(generator()).take(2).mergeMap(x => x).subscribe(
x => console.log(`Process took: ${x}msec`),
e => console.log(`Error: ${e}`),
c => console.log('Complete')
)
Which results is the output:
Yielding...
Taking 2698msec to process...
Yielding...
Taking 2240msec to process...
Process took: 2240msec
Process took: 2698msec
Complete
The yields occur right away. The Process took: xxx output occurs when you'd expect (after 2240 and 2698ms, respectively).
(In all fairness, the reason I care about the delay in between yields is that asyncOperation() here is in reality a rate-limiting token bucket library which controls the rate of asynchronous callbacks - an implementation which I'd like to retain.)
As an aside, I attempted to replace take(2) with a delayed cancellation, but that never occurred:
const subscription = Rx.Observable.from(generator()).mergeMap(x => x).subscribe(
x => console.log(`Process took: ${x}msec`),
e => console.log(`Error: ${e}`),
c => console.log('Complete')
)
console.log('Never gets here?');
timers.setTimeout(() => {
console.log('Cancelling...');
subscription.unsubscribe();
}, 0);
Can what I'm attempting be accomplished with a cancellable subscription via RxJS? (I can see other approaches, such as process.exec('node', ...) to run asyncOperation() as a separate process, giving me the ability to process.kill(..), etc., but let's not go there...).
Is my initial callback-based implementation the suggested way to implement a cancellable sequence?
UPDATED SOLUTION:
See my reply comment to #user3743222's answer below. Here's what I ended up with (replace ES6 generator with Observable.expand()):
'use strict';
const timers = require('timers');
const Rx = require('rxjs/Rx');
function asyncOperation(callback) {
const delayMsec = Math.floor(Math.random() * 10000) + 1;
console.log(`Taking ${delayMsec}msec to process...`);
timers.setTimeout(callback, delayMsec, null, delayMsec);
}
const operationAsObservable = Rx.Observable.bindNodeCallback(asyncOperation);
const subscription = Rx.Observable
.defer(operationAsObservable)
.expand(x => operationAsObservable())
.subscribe(
x => console.log(`Process took: ${x}msec`),
e => console.log(`Error: ${e}`),
c => console.log('Complete')
);
subscription.add(() => {
console.log('Cancelled');
});
timers.setTimeout(() => {
console.log('Cancelling...');
subscription.unsubscribe();
}, 0);
UPDATED SOLUTION 2:
Here's what I came up with for the alternate RxJS4 repeatWhen() approach:
'use strict';
const timers = require('timers');
const Rx = require('rx');
function asyncOperation(callback) {
const delayMsec = Math.floor(Math.random() * 1000) + 1;
console.log(`Taking ${delayMsec}msec to process...`);
timers.setTimeout(callback, delayMsec, null, delayMsec);
}
const operationAsObservable = Rx.Observable.fromNodeCallback(asyncOperation);
const subscription = Rx.Observable
.defer(operationAsObservable)
.repeatWhen(x => x.takeWhile(y => true))
.subscribe(
x => console.log(`Process took: ${x}msec`),
e => console.log(`Error: ${e}`),
c => console.log('Complete')
);
timers.setTimeout(() => {
console.log('Cancelling...');
subscription.dispose();
}, 10000);
You seem to be repeating an action every time it finishes. That looks like a good use case for expand or repeatWhen.
Typically, that would be something like :
Rx.Observable.just(false).expand(_ => {
return cancelled ? Rx.Observable.empty() : Rx.Observable.fromCallback(asyncAction)
})
You put cancelled to true at any point of time and when the current action finishes, it stops the loop. Haven't tested it so I would be interested to know if that worked in the end.
You can have a look at similar questions about polling:
How to build an rx poller that waits some interval AFTER the previous ajax promise resolves?
Documentation:
[fromCallback]
[expand]
Documentation links are for Rxjs 4 but there should not be much changes vs v5

RxJS: show loading if request is slow

I thought of using RxJS to solve elegantly this problem, but after trying various approaches, I couldn't find out how to do it...
My need is quite common: I do a Rest call, ie. I have a Promise.
If the response comes quickly, I just want to use the result.
If it is slow to come, I want to display a spinner, until the request completes.
This is to avoid a flash of a the spinner, then the data.
Maybe it can be done by making two observables: one with the promise, the other with a timeout and showing the spinner as side effect.
I tried switch() without much success, perhaps because the other observable doesn't produce a value.
Has somebody implemented something like that?
Based on #PhiLho's answer, I wrote a pipeable operator, which does exactly that:
export function executeDelayed<T>(
fn : () => void,
delay : number,
thisArg? : any
) : OperatorFunction<T, T> {
return function executeDelayedOperation(source : Observable<T>) : Observable<T> {
let timerSub = timer(delay).subscribe(() => fn());
return source.pipe(
tap(
() => {
timerSub.unsubscribe();
timerSub = timer(delay).subscribe(() => fn());
},
undefined,
() => {
timerSub.unsubscribe();
}
)
);
}
}
Basically it returns a function, which gets the Observable source.
Then it starts a timer, using the given delay.
If this timer emits a next-event, the function is called.
However, if the source emits a next, the timer is cancelled and a new one is startet.
In the complete of the source, the timer is finally cancelled.
This operator can then be used like this:
this.loadResults().pipe(
executeDelayed(
() => this.startLoading(),
500
)
).subscribe(results => this.showResult())
I did not wirte many operators myself, so this operator-implementation might not be the best, but it works.
Any suggestions on how to optimize it are welcome :)
EDIT:
As #DauleDK mentioned, a error won't stop the timer in this case and the fn will be called after delay. If thats not what you want, you need to add an onError-callback in the tap, which calls timerSub.unsubscribe():
export function executeDelayed<T>(
fn : () => void,
delay : number,
thisArg? : any
) : OperatorFunction<T, T> {
return function executeDelayedOperation(source : Observable<T>) : Observable<T> {
let timerSub = timer(delay).subscribe(() => fn());
return source.pipe(
tap(
() => {
timerSub.unsubscribe();
timerSub = timer(delay).subscribe(() => fn());
},
() => timerSub.unsubscribe(), // unsubscribe on error
() => timerSub.unsubscribe()
)
);
}
}
Here is an example that I have used. We assume here that you get the data that you want to send to the server as an Observable as well, called query$. A query coming in will then trigger the loadResults function, which should return a promise and puts the result in the results$ observable.
Now the trick is to use observable$.map(() => new Date()) to get the timestamp of the last emitted value.
Then we can compare the timestamps of the last query and the last response that came in from the server.
Since you also wanted to not only show a loading animation, but wanted to wait for 750ms before showing the animation, we introduce the delayed timestamp. See the comments below for a bit more explanation.
At the end we have the isLoading$ Observable that contains true or false. Subscribe to it, to get notified when to show/hide the loading animation.
const query$ = ... // From user input.
const WAIT_BEFORE_SHOW_LOADING = 750;
const results$ = query$.flatMapLatest(loadResults);
const queryTimestamp$ = query$.map(() => new Date());
const resultsTimestamp$ = results$.map(() => new Date());
const queryDelayTimestamp$ = (
// For every query coming in, we wait 750ms, then create a timestamp.
query$
.delay(WAIT_BEFORE_SHOW_LOADING)
.map(() => new Date())
);
const isLoading$ = (
queryTimestamp$.combineLatest(
resultsTimestamp$,
queryDelayTimestamp$,
(queryTimestamp, resultsTimestamp, delayTimestamp) => {
return (
// If the latest query is more recent than the latest
// results we got we can assume that
// it's still loading.
queryTimestamp > resultsTimestamp &&
// But only show the isLoading animation when delay has passed
// as well.
delayTimestamp > resultsTimestamp
);
}
)
.startWith(false)
.distinctUntilChanged()
);
OK, thinking more about it in my commuting, I found a solution...
You can find my experiment ground at http://plnkr.co/edit/Z3nQ8q
In short, the solution is to actually subscribe to the observable handing the spinner (instead of trying to compose it in some way).
If the result of the Rest request comes before the observable fires, we just cancel the spinner's disposable (subscription), so it does nothing.
Otherwise, the observable fires and display its spinner. We can then just hide it after receiving the response.
Code:
function test(loadTime)
{
var prom = promiseInTime(loadTime, { id: 'First'}); // Return data after a while
var restO = Rx.Observable.fromPromise(prom);
var load = Rx.Observable.timer(750);
var loadD = load.subscribe(
undefined,
undefined,
function onComplete() { show('Showing a loading spinner'); });
restO.subscribe(
function onNext(v) { show('Next - ' + JSON.stringify(v)); },
function onError(e) { show('Error - ' + JSON.stringify(e)); loadD.dispose(); },
function onComplete() { show('Done'); loadD.dispose(); }
);
}
test(500);
test(1500);
Not sure if that's an idiomatic way of doing this with RxJS, but it seems to work...
Other solutions are welcome, of course.
Just before fetching the data, ie. creating the spinner, set timeout for a function, which creates the spinner. Lets say you are willing to wait half a second, until showing spinner... it would be something like:
spinnerTimeout = setTimeout(showSpinner, 500)
fetch(url).then(data => {
if (spinner) {
clearTimeout(spinnerTimeout) //this is critical
removeSpinnerElement()
}
doSomethingWith(data)
});
EDIT: if it's not obvious, clearTimer stops the showSpinner from executing, if the data arrived sooner than 500ms(ish).
Here is my solution :
public static addDelayedFunction<T>(delayedFunction: Function, delay_ms: number): (mainObs: Observable<T>) => Observable<T> {
const stopTimer$: Subject<void> = new Subject<void>();
const stopTimer = (): void => {
stopTimer$.next();
stopTimer$.complete();
};
const catchErrorAndStopTimer = (obs: Observable<T>): Observable<T> => {
return obs.pipe(catchError(err => {
stopTimer();
throw err;
}));
};
const timerObs: Observable<any> = of({})
.pipe(delay(delay_ms))
.pipe(takeUntil(stopTimer$))
.pipe(tap(() => delayedFunction()));
return (mainObs: Observable<T>) => catchErrorAndStopTimer(
of({})
.pipe(tap(() => timerObs.subscribe()))
.pipe(mergeMap(() => catchErrorAndStopTimer(mainObs.pipe(tap(stopTimer)))))
);
}

Categories