Consider this Observable:
myObs$ = interval(1000).pipe(shareReplay(2));
And its usage:
myObs$.subscribe(res => console.log('[Subscriber 1]:' + res));
If I subscribe, then unsubscribe a few seconds later, and finally resubscribe again after another few seconds, it seems like the interval kept running and counting:
[Subscriber 1]: 0
[Subscriber 1]: 1
Unsubscribe, resubscribe here
[Subscriber 1]: 3
[Subscriber 1]: 4
[Subscriber 1]: 5
[Subscriber 1]: 6
Unsubscribe, resubscribe here
[Subscriber 1]: 10
[Subscriber 1]: 11
[Subscriber 1]: 12
I understood that this does not happen when using refCount = true. But when it is false, does this count as a potential memory leak ? If not, how do I stop it?
Also, why do I need to recreate my Subscription after unsubscribing?
sub = new Subscription()
sub.add(myObs.subscribe())
sub.unsubscribe()
sub.add(myObs.subscribe()) // <-- this does not work unless I recreate a new Subscription()
Using shareReply without refCount can result in a memory leak because the operator doesn't close automatically the stream after everyone has unsubscribed. You can make a long lived stream complete by using the takeUntil operator which accepts another stream and it's very useful when used with a Subject.
const sub = new Subject();
const stream$ = interval(1000).pipe(
takeUntil(sub),
shareReplay(1)
);
setTimeout(() => stream$.subscribe(console.log), 4000);
setTimeout(() => sub.next(), 6000);
setTimeout(() => stream$.subscribe(undefined, undefined, () => console.log('completed')), 8000); // this will log completed because the stream$ has successfully finished
Another way to complete a stream is with takeWhile which is very handy as well and it uses a predicate function.
If you dispose a subscription it is marked as closed and you need to create a new one, it cannot be reused again.
Take a look on this line where inside the add method there is a check and if closed is true just executes the teardown on the provided subscription.
Related
I am struggling hard to properly test this scenario and could not really match the numbers. Could you please verify whether this configuration is correct for the below scenario?
When the message comes to consumer first time, want to retry for these exceptions WebException, HttpRequestException, RequestTimeoutException, TimeoutException. And after this retries exhausted I want to redeliver these messages( only for the above exceptions) using delayed exchange with intervals first time delay 2 minutes, then 4 minutes and finally 6 minutes and after 3 times stop redeliver and push to _error queue.
I want UseMessageRetry() should execute only first time and not every time when the message get to consumer through delayed exchange.
cfg.UseDelayedExchangeMessageScheduler();
cfg.ReceiveEndpoint(rabbitMqConfig.QueueName, e =>
{
e.PrefetchCount = 20;
e.UseRateLimit(100, TimeSpan.FromMinutes(3));
e.UseDelayedRedelivery(p =>
{
p.Intervals(TimeSpan.FromMinutes(2), TimeSpan.FromMinutes(4),TimeSpan.FromMinutes(6));
});
e.UseCircuitBreaker(cb =>
{
cb.TrackingPeriod = TimeSpan.FromMinutes(1);
cb.TripThreshold = 15;
cb.ActiveThreshold = 10;
cb.ResetInterval = TimeSpan.FromMinutes(5);
});
e.UseMessageRetry(r =>
{
r.Incremental(2, TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(6));
r.Handle<WebException>();
r.Handle<HttpRequestException>();
r.Handle<TimeoutException>();
r.Handle<RequestTimeoutException>();
});
e.Consumer<Consumers.ProductConsumer>(provider);
});
var button = document.querySelector('button');
var obs1 = Rx.Observable.fromEvent(button, 'click').scan(count => count + 1, 0).multicast(new Rx.Subject())
function obs2$(result) {
return Rx.Observable.of(result).delay((Math.random() * 10000 % 2000) + 1000)
}
obs1.concatMap((count) => {
return obs2$(count)
}).subscribe(x => console.log("Sync Finished " + x))
obs1.subscribe(x => console.log("Sync Request " + x))
obs1.connect()
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/#reactivex/rxjs#5.3.0/dist/global/Rx.js"></script>
<button>Click me</button>
<h3 id='show'>Foo</h3>
I am implementing a syncing system with two observables in play. One is just a stream of sync request actions:
actions$.ofType('SYNC_REQUESTED')
actions$ in this case is a redux-observable construct representing Redux actions.
The other observable performs the actual sync and dispatches a Redux action when it is completed.
I want it to behave such that if a sync is currently being performed, and another emission is made to the sync request observable, the sync gets queued up (like in the case of concatMap). However, if multiple emissions are made during a sync, only one of them is actually taken.
In other words, I want a switchMap that doesn't cancel the current sync.
If you run the attached snippet and click the button five times in a row within a second you'll see:
Sync Request 1
Sync Request 2
Sync Request 3
Sync Request 4
Sync Request 5
Sync Finished 5
If you use switchMap.
And using concatMap you'll see:
Sync Request 1
Sync Request 2
Sync Request 3
Sync Request 4
Sync Request 5
Sync Finished 1
Sync Finished 2
Sync Finished 3
Sync Finished 4
Sync Finished 5
I want it read:
Sync Request 1
Sync Request 2
Sync Request 3
Sync Request 4
Sync Request 5
Sync Finished 1
Sync Finished 5
Now because I am using Redux I could always use the store to help me out, maybe keeping track of syncPending and syncInProgress flags and using skipWhile to skip sync requests that happen while a sync is in progress if there's already one pending.
I was wondering if there was a way to do this just by using RxJS though since that would require extra code / actions / reducers / Redux state.
try take(1) and repeat()
var button = document.querySelector('button');
var obs1 = Rx.Observable.fromEvent(button, 'click').scan(count => count + 1, 0).multicast(new Rx.Subject())
function obs2$(result) {
return Rx.Observable.of(result).delay(2000)
}
Rx.Observable.merge(obs1.audit(()=>obs1.mergeMap(obs2$)),
obs1.take(1).mergeMap(obs2$).repeat())
.subscribe(console.log)
obs1.subscribe(x => console.log("Sync Request " + x))
obs1.connect()
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/#reactivex/rxjs#5.3.0/dist/global/Rx.js"></script>
<button>Click me</button>
<h3 id='show'>Foo</h3>
I'm working on a use case that requires that if an observable has not emitted a value within a certain amount of time then we should do some side effect.
To give a practical use case:
open web socket connection
if no message has been sent/received within X time then close web socket connection and notify user
This requires for a timer to be initiated on every emitted value and upon initial subscription of observable which will then run some function after the allotted time or until a value is emitted in which the timer resets. I'm struggling to do this the Rx way. Any help would be appreciated :)
debounceTime is the operator you're looking for: it only emits a value if no others follow within a specific timeout. Listening for the first message of the debounced stream will let you time out and clean up your websocket connection. If you need to time out starting from the opening of the stream, you can simply startWith. Concretely:
messages$.startWith(null)
.debounceTime(timeout)
.take(1)
.subscribe(() => { /* side effects */ });
Edit: if instead you're looking to end the a message stream entirely when it times out (e.g. you clean up in the onComplete handler), just cram debounceTime into a takeUntil:
messages$.takeUntil(
messages$.startWith(null)
.debounceTime(timeout)
).subscribe(timeout_observer);
With a timeout_observable: Observer<TMessage> that contains your cleanup onComplete.
You can do this with race:
timer(5000).race(someSource$)
.subscribe(notifyUser);
If someSource$ notifies faster than timer(5000) (5 seconds), then someSource$ "wins" and lives on.
If you only want one value from someSource$, you can obviously have a take(1) or first() on someSource$ and that will solve that issue.
I hope that helps.
Might not be the perfect answer but it does what you asked, it depends on how you want to disconnect, there might be some variation to be done
const source = new Rx.Subject();
const duration = 2000;
source.switchMap(value=>{
return Rx.Observable.of(value).combineLatest(Rx.Observable.timer(2000).mapTo('disconnect').startWith('connected'))
}).flatMap(([emit,timer])=>{
if(timer=='disconnect'){
console.log('go disconnect')
return Rx.Observable.throw('disconnected')
}
return Rx.Observable.of(emit)
})
//.catch(e=>Rx.Observable.of('disconnect catch'))
.subscribe(value=>console.log('subscribed->',value),console.log)
setTimeout(() => source.next('normal'), 300);
setTimeout(() => source.next('normal'), 300);
setTimeout(() => source.next('last'), 1800);
setTimeout(() => source.next('ignored'), 4000);
<script src="https://unpkg.com/rxjs#5/bundles/Rx.min.js"></script>
A timer is initiated on each element and if it takes 4 seconds to be shown, then it will timeout and you can execute your function in the catchError
Here an example, it displays aa at T0s, then bb at t3s, then timeout after 4 second because the last one cc takes 10s to be displayed
import './style.css';
screenLog.init()
import { from } from 'rxjs/observable/from';
import { of } from 'rxjs/observable/of';
import { race } from 'rxjs/observable/race';
import { timer } from 'rxjs/observable/timer';
import { groupBy, mergeMap, toArray, map, reduce, concatMap, delay, concat, timeout, catchError, take } from 'rxjs/operators';
// simulate a element that appear at t0, then at t30s, then at t10s
const obs1$ = of('aa ');
const obs2$ = of('bb ').pipe(delay(3000));
const obs3$ = of('cc ').pipe(delay(10000));
const example2 = obs1$.pipe(concat(obs2$.pipe(concat(obs3$))), timeout(4000), catchError(a => of('timeout'))); // here in the catchError, execute your function
const subscribe = example2.subscribe(val => console.log(val + ' ' + new Date().toLocaleTimeString()));
I started using Bluebird and I see that it changes the structure of the promise.
Everything is now with an underscore so it's private (?), so what indicates if the promise was fulfilled or failed or pending?
In contrast with the original structure:
Lets have 3 promises - 2 resolved and 1 in rejected state and mix them with one another promise - timeout that will be rejected after 1 second.
Promise.race returns promise as soon one of the promises in the given array resolves or rejects.
const Promise = require("bluebird");
let p1 = Promise.resolve('first')
let p2 = new Promise((resolve) => {
setTimeout(resolve, 1e8)
})
let p3 = Promise.resolve('third')
Promise.race([
Promise.all([p1, p2, p3]).then(() => console.log('ok')),
new Promise((resolve, reject) => setTimeout(reject, 1e3)) // rejected after 1000 ms
])
.catch(() => console.log(`Promise p2 is in pending state: ${p2.isPending()}`))
.catch() will logs Promise p2 is in pending state: true
I've been struggling to come up with an Rx strategy for a particular situation. I'm hoping someone could point me in the right direction.
Basically, I have a socket feed that I would like to skip based on a boolean value. When the stream is skipping the socket, I need to keep a running buffer of the latest value sent from socket.
Once I am no longer skipping the socket events, then push down the stream the last value that was emitted when it was skipping, but only under another condition (bool), and re-start listen to socket events
So basically:
Listen to socket feed
takeWhile(bool)
When start listening again, apply last values while skipping
socket, if reapply==true
Didn't get far, but this is what I have:
Rx.Observable.interval(1000)
.skipWhile(()=>isSkipping)
.bufferWhileSkipping??
.applySkippedValuesAfterSkipping(ifisReapply)??
.subscribe(val=>console.log(val));
Perhaps skipWhile is not the right approach but was the only one that kind of made senseā¦
You could do it similarly to the following (I'm assuming your isSkipping can be an Observable):
const isSkipping = new BehaviorSubject(false);
Observable.interval(100)
.take(20)
.window(isSkipping)
.withLatestFrom(isSkipping)
.switchMap(([observable, skipping]) => skipping
? observable.takeLast(1).map(val => 'last:' + val)
: observable)
.subscribe(console.log);
setTimeout(() => isSkipping.next(true), 500);
setTimeout(() => isSkipping.next(false), 1050);
setTimeout(() => isSkipping.next(true), 1500);
setTimeout(() => isSkipping.next(false), 1850);
Every time isSkipping emits a value the window operator creates a new Observable that just re-emits everything or chains the .takeLast(1) operator when skipping is set to true.
The example above prints the following output to the console:
0
1
2
3
last:9
10
11
12
13
last:16
17
18
19