简体   繁体   中英

How can I unsubscribe or cancel the filtering of a large array that is an RxJS observable?

My understanding is that an entire array is pushed to a subscriber, unlike say an interval observer that can be unsubscribed/cancelled.

For example the following cancellation works...

 // emit a value every second for approx 10 seconds let obs = Rx.Observable.interval(1000) .take(10) let sub = obs.subscribe(console.log); // but cancel after approx 4 seconds setTimeout(() => { console.log('cancelling'); sub.unsubscribe() }, 4000); 
 <script src="https://unpkg.com/rxjs@5.5.10/bundles/Rx.min.js"></script> 

However, replacing the interval with an array doesn't.

 // emit a range let largeArray = [...Array(9999).keys()]; let obs = Rx.Observable.from(largeArray) let sub = obs.subscribe(console.log); // but cancel after approx 1ms setTimeout(() => { console.log('cancelling'); sub.unsubscribe() }, 1); // ... doesn't cancel 
 <script src="https://unpkg.com/rxjs@5.5.10/bundles/Rx.min.js"></script> 

Does each element need to be made asynchronous somehow, for example by wrapping it in setTimeout(..., 0)? Perhaps I've been staring at this problem too long and I'm totally off course in thinking that the processing of an array can be cancelled?

When using from(...) on an array all of the values will be emitted synchronously which doesn't allow any execution time to be granted to the setTimeout that you are using to unsubscribe. Infact, it finishes emitting before the line for the setTimeout is even reached. To allow the emits to not hog the thread you could use the async scheduler ( from(..., Rx.Scheduler.async) ) which will schedule work using setInterval .

Here are the docs: https://github.com/ReactiveX/rxjs/blob/master/doc/scheduler.md#scheduler-types

Here is a running example. I had to up the timeout to 100 to allow more room to breath. This will slow down your execution of-course. I don't know the reason that you are attempting this. We could probably provide some better advice if you could share the exact use-case.

 // emit a range let largeArray = [...Array(9999).keys()]; let obs = Rx.Observable.from(largeArray, Rx.Scheduler.async); let sub = obs.subscribe(console.log); // but cancel after approx 1ms setTimeout(() => { console.log('cancelling'); sub.unsubscribe() }, 100); // ... doesn't cancel 
 <script src="https://unpkg.com/rxjs@5.5.10/bundles/Rx.min.js"></script> 

I've marked @bygrace's answer correct. Much appreciated! As mentioned in the comment to his answer, I'm posting a custom implementation of an observable that does support such cancellation for interest ...

 const observable = stream => { let timerID; return { subscribe: observer => { timerID = setInterval(() => { if (stream.length === 0) { observer.complete(); clearInterval(timerID); timerID = undefined; } else { observer.next(stream.shift()); } }, 0); return { unsubscribe: () => { if (timerID) { clearInterval(timerID); timerID = undefined; observer.cancelled(); } } } } } } // will count to 9999 in the console ... let largeArray = [...Array(9999).keys()]; let obs = observable(largeArray); let sub = obs.subscribe({ next: a => console.log(a), cancelled: () => console.log('cancelled') }); // except I cancel it here setTimeout(sub.unsubscribe, 200); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM