I am trying to work on Subject from RxJS. In below 2 code snippets the output is different. I couldn't understand why 'subscribe' inside 'setTimeout' method is not logging the value. Could anyone please help me through it? Thank you.
import { Subject, from, Observable } from 'rxjs';
const subject = new Subject();
subject.subscribe((value) => console.log(value));
setTimeout(() => {
subject.subscribe((value) => console.log(value));
}, 2000);
const observable = new Observable((observer) => {
console.log('called only once')
observer.next(Math.random())
});
observable.subscribe(subject);
Output:
called only once
0.253655945545456
=================================================
import { Subject, from, Observable } from 'rxjs';
const subject = new Subject();
subject.subscribe((value) => console.log(value));
subject.subscribe((value) => console.log(value));
const observable = new Observable((observer) => {
console.log('called only once')
observer.next(Math.random())
});
observable.subscribe(subject);
Output:
called only once
0.253655945545456
0.253655945545456
Related
How to do unsubscribe when I got error in the pipeline?
I do subscribe to the subjectPipe pipeline outside doWork function.
Inside doWork function I throw an error (can be as part of my logic flow).
Everything works as expected but subjectPipe is log: "in subject error" and because I using take(1) I thought it should unsubscribe, But it does not happen because the complete function is not invoke.
How to make it unsubscribe when error happens?
The only thing I can think of is to create variable error$ and use takeUntil and then on error function to invoke error$. but I don't want to do it because is extra variable and the code will be messy if I do it for every case I have.
stackblitz.com
import { Subject, throwError } from 'rxjs';
import { map, switchMap, take, tap } from 'rxjs/operators';
console.clear();
const doWork = () => {
const subject = new Subject();
const work = () => {
throw new Error('bla');
};
const subjectPipe = subject.pipe(
tap(() => {
console.log('in subject pipeline');
}),
switchMap(() => work())
);
subjectPipe.subscribe({
next: () => console.log('in subjectPipe next'),
error: () => console.log('in subjectPipe error'),
complete: () => console.log('in subjectPipe complete'),
});
return { subject, subjectPipe };
};
const { subject, subjectPipe } = doWork();
subjectPipe
.pipe(
take(1),
tap(() => console.log('continue the pipe...'))
)
.subscribe({
next: () => console.log('in subject next'),
error: () => console.log('in subject error'),
complete: () => console.log('in subject complete'),
});
subject.next(null);
The error means completion (however, the callback associated with complete never gets called). So, there is no need to unsubscribe from the stream manually here.
In your case, if you need to call some logic in both completion and error, you can use the RxJS finalize operator, which will be called in both cases.
subjectPipe
.pipe(
take(1),
tap(() => console.log('continue the pipe...')),
finalize(() =>
console.log(
'You can do the final stuff here, instead of `complete` callback'
)
)
)
.subscribe({
next: () => console.log('in subject next'),
error: () => console.log('in subject error'),
complete: () => console.log('in subject complete'),
});
I have an Issue with timeout operator in subject.
my problem look like sample above, but I got all of source,
response:
observable: 1
observable: 2
url: https://stackblitz.com/edit/ou5yp1?file=index.ts
import { Subject, from,Observable } from 'rxjs';
import {timeout} from 'rxjs/operators';
const subject = new Subject();
subject.subscribe({
next: (v) => console.log(`observer: ${v}`),
error: (e) => console.log(`There is an Error ${e}`)
});
subject.pipe(timeout(2000));
const observable = new Observable( sub=> {
sub.next( 1);
setTimeout(()=> {
sub.next(2)
},5000)
})
observable.subscribe(subject);
You have subscribed to wrong observable.
subject.pipe(timeout(2000));
The above line does not apply to subject itself, but instead returns a new observable which has 2 seconds timeout. So you should subscribe to this returned observable instead of subject itself. So your code should be:
subject.pipe(timeout(2000)).subscribe({
next: (v) => console.log(`observer: ${v}`),
error: (e) => console.log(`There is an Error ${e}`)
});
I am modelling a reactivity handler from rxjs and Golang channels. I am trying to use iterators to achieve this however my "Channel" only emits when an async event is supplied. Why is that?
Here is my consumer code
import { Channel } from "./channel";
const numbers: any = new Channel();
void async function() {
for (const number of numbers) {
// Will pause and wait for emit()
console.log(await number);
}
}();
numbers.emit(1);
numbers.emit(2);
numbers.emit(3);
setTimeout(() => numbers.emit(4));
setTimeout(() => numbers.emit(5), 50);
setTimeout(() => numbers.emit(6), 100);
This is my "Channel" implementation.
export class Channel<T = any> {
private onValue = new PromiseSubject()
emit(value: T): void {
this.onValue.resolve(value)
this.onValue = new PromiseSubject()
}
*[Symbol.iterator](): Iterator<Promise<T>> {
while (true) {
yield this.onValue.promise
}
}
}
export class PromiseSubject<T = any> {
public resolve!: (value?: T) => void
public promise = new Promise<T>((res) => this.resolve = res)
}
I expect an output of 123456 however I only get 456. It seems when events are bunched together the iterator doesn't yield a value.
Similarly, I get no console output when I run:
setTimeout(() => {
numbers.emit(1)
numbers.emit(2)
numbers.emit(3)
})
Sandbox link:
https://codesandbox.io/s/festive-wave-y0o3e?expanddevtools=1&fontsize=14&hidenavigation=1&theme=dark
Because you need to implement somekind of queue.
With
this.onValue = new PromiseSubject()
you override the current onValue, when you call the iterator (with for of) you only await the current onValue, not the promises stored there before.
There are however async iterators, that do exactly what you are looking for!
export class Channel<T = any> {
toEmit = [] as T[];
resolveLast: Promise?;
emit(value: T): void {
if(this.resolveLast) this.resolveLast(value);
else this.toEmit.push(value);
}
async *[Symbol.iterator](): {
while (true) {
const value = this.toEmit.shift();
yield value;
if(this.toEmit.length === 0)
await new Promise(res => this.resolveLthis.resolveLast = res);
}
}
}
// iterable as
for await(const el of new Channel)
Recommended reading:
Jake Archibald on async iterators
I have this method that I copied from a websocket tutorial but I don't understand the meaning of the "return () => { ... }" inside the observable ? Can someone explain me what is the purpose of that ?
public onMessage(topic: string, handler = SocketClientService.jsonHandler) : Observable<any> {
return this.connect().pipe(first(), switchMap(client => {
return new Observable<any>(observer => {
const subscription : StompSubscription = client.subscribe(topic, message => {
observer.next(handler(message));
});
return () => {
console.log("Unsubscribe from socket-client service");
client.unsubscribe(subscription .id);
}
});
}));
}
In order to create an Observable, you can use new Observable or a creation operator. See the following example:
const observable = new Observable(function subscribe(subscriber) {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
});
You can provide a function unsubscribe() to allow dispose of resources, and that function goes inside subscribe() as follows:
const observable = new Observable(function subscribe(subscriber) {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
return function unsubscribe() {
console.log('Clearing resources on observable');
};
});
Of course, you can use an arrow function expression to have:
const observable = new Observable((observer) => {
observer.next(1);
observer.next(2);
observer.next(3);
return () => {
console.log('Clearing resources on observable');
};
});
Try the following code to test the Observable:
const subscription = observable.subscribe(res => console.log('observable data:', res));
subscription.unsubscribe();
Finally, subscription.unsubscribe() is going to remove the socket connection in your example.
Find a project running with these examples here: https://stackblitz.com/edit/typescript-observable-unsubscribe
Let me know if that helps!
I want to have an observable that when unsubscribed it calls a function but only when it is unsubscribed without error and without getting to complete. The observable I am trying to build usually gets raced with another observable. I want when the other observable "wins" this one executes a function.
I tried finalize operator but it executes always.
playback.ts
import { timer } from "rxjs";
import { takeUntil, finalize } from "rxjs/operators";
import errorobs$ from "./errorobs";
export default function() {
return timer(10000).pipe(
takeUntil(errorobs$),
finalize(finalFunc)
);
}
function finalFunc() {
console.log("final function executed");
}
errorobs.ts
import { fromEvent } from "rxjs";
import { map } from "rxjs/operators";
export default fromEvent(document.getElementById("errorBtn"), "click").pipe(
map(() => {
throw new Error("my error");
})
);
I have made a small demo here https://codesandbox.io/s/q7pwowm4l6
click start to start "the observable".
click cancel to make the other observable win
click error to generate an error
One way to achieve this is using a custom operator, like my onCancel() below:
const {Observable} = rxjs
function onCancel(f) {
return observable => new Observable(observer => {
let completed = false
let errored = false
const subscription = observable.subscribe({
next: v => observer.next(v),
error: e => {
errored = true
observer.error(e)
},
complete: () => {
completed = true
observer.complete()
}
})
return () => {
subscription.unsubscribe()
if (!completed && !errored) f()
}
})
}
// Test:
const {interval} = rxjs
const {take} = rxjs.operators
// This one gets cancelled:
const s = interval(200).pipe(
onCancel(() => console.warn('s cancelled!'))
).subscribe(() => {})
setTimeout(() => s.unsubscribe(), 500)
// This one completes before unsubscribe():
const q = interval(200).pipe(
take(2),
onCancel(() => console.warn('q cancelled!'))
).subscribe(() => {})
setTimeout(() => q.unsubscribe(), 500)
<script src="//unpkg.com/rxjs#6/bundles/rxjs.umd.min.js"></script>
It really works as you describe it. finalize is executed when the chain is being disposed which is when all subscribers unsubscribe, when the chain errors or when it completes.
There's already an issue on RxJS Github page for this feature: https://github.com/ReactiveX/rxjs/issues/2823
In the link above you can see an example of a custom operator that adds reason to the finalize operator.
I had to deal with this use-case myself and added this operator to my own collection of RxJS operators: https://github.com/martinsik/rxjs-extra/blob/master/doc/finalizeWithReason.md