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}`)
});
Related
I want to check one api call inside an observable which I will subscribe in a component. As written below, I want to run my observable in this manner but it is not working. What changes shall I do to this code to make it work. Whenever I try to subscribe through it especially through the scenario when someObservableWrittenInTheSameService returns with an error 404, I want to return url2.
getfunction(submissionId: string ){
if (some condition) {
this.someObservableWrittenInTheSameService(parameter).subscribe(
(httpValue: any) => {
let url = '';
if (httpValue.code === 200) {
return this.http.get(url1);
}
}, err => {
if (err.code === 404) {
return this.http.get(url2);
}
}
)
}
let url3
return this.http.get(url3);
}
This function is then is called in a component where it is subscribed. But whenever someObservableWrittenInTheSameService return 404, the subscription always fails and go to error block in the component.
You could use RxJS iif function to return an observable conditionally.
Use RxJS higher order mappping operator switchMap to map from one observable to another. More info here.
Use catchError operator to perform error handling. From it's body you could either return the HTTP request or forward the error (using throwError) or even complete the observable (using EMPTY constant) based on your requirement.
Try the following
import { Observable, EMPTY, iif, throwError } from 'rxjs';
import { switchMap, catchError } from 'rxjs/operators';
getfunction(submissionId: string): Observable<any> { // <-- observable must be returned here
const obs1$ = this.someObservableWrittenInTheSameService(parameter).pipe(
switchMap((httpValue: any) =>
iif(
() => httpValue.code === 200,
this.http.get(url1),
EMPTY // <-- complete the observable if code is other than 200
)
),
catchError((error: any) => // <-- `catchError` operator *must* return an observable
iif(
() => error.code === 404,
this.http.get(url2),
throwError(error) // <-- you could also return `EMPTY` to complete the observable
)
)
const obs2$ = this.http.get(url3);
return iif(
() => someCondition,
obs1$,
obs2$
);
}
In this case you'd subscribe to the getFunction() function where it's used.
For eg.
this.getFunction('some value').subscribe({
next: (value: any) => { },
error: (error: any) => { },
complete: () => { }
});
Contrived example of what I'm trying to do here:
const source = interval(1000);
const errorWhenThisEmits = timer(2500);
source.pipe(/* someOperatorHere? */).subscribe({
next: () => console.log('next!'),
error: (err) => console.log(err)
});
// desired outcome:
// 1000ms: next!
// 2000ms: next!
// 2500ms: error message
Is it possible to cause the source observable to error when the second observable emits a value?
takeUntil gets close, but completes instead of errors.
You could merge the observables
const source = interval(1000);
const notifier = timer(2500).pipe(switchMap(() => throwError("error message")));
merge(source, notifier).subscribe({
next: () => console.log("next!"),
error: err => console.log(err)
});
See stackblitz: https://stackblitz.com/edit/rxjs-ony9vx?file=index.ts
Discovered takeUntil will error the parent observable if the notifier observable errors.
const source = interval(1000);
// added switchMap to throwError
const notifier = timer(2500).pipe(switchMap(() => throwError('error!')));
source.pipe(takeUntil(notifier)).subscribe({
next: (data) => console.log(data),
error: (err) => console.log(err)
});
This outputs:
0
1
error!
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
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
I'm trying to throw an error inside a rxjs Observable
new Observable(subscriber => {
Observable.throw("error1");
return Observable.throw("error2");
})
.subscribe(
() => {},
err => console.error(err)
);
error 1 is not caught.
error2 gives a compilation error:
Argument of type '(this: Observable<{}>, subscriber: Subscriber<{}>) => ErrorObservable<string>' is not assignable to parameter of type '(this: Observable<{}>, subscriber: Subscriber<{}>) => TeardownLogic'. Type 'ErrorObservable<string>' is not assignable to type 'TeardownLogic'
what is the proper way to throw an error inside an observable?
Use Subscriber.error:
new Rx.Observable(subscriber => {
subscriber.error("error1");
subscriber.error("error2"); // not called because a stream will be finalized after any error
})
.subscribe(
() => {},
err => console.error(err)
);
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>
When using new Observable() or Observable.create() you can push the error directly to the subscriber (observer):
Observable.create(subscriber => {
subscriber.error(new Error("error1"));
})
.subscribe(
() => {},
err => console.log(err.message)
);
The anonymous function passed to new Observable() can optionally return an unsubscribe callback. That's why it gives you the error because you're returning an Observable.
Using Observable.throw("error1") is usually not necessary. This is just an Observable that only emits an error and nothing more. You could use it for example like the following:
Observable.create(subscriber => {
subscriber.next('whatever');
})
.merge(Observable.throw(new Error("error2")))
.subscribe(
() => {},
err => console.log(err.message)
);
See live demo: https://jsbin.com/fivudu/edit?js,console
In v6 and higher you want to import throwError, that Observable.throw static method is an unfortunate necessity leftover from v5 compat.
import { throwError } from 'rxjs';
throwError('hello');
Source: https://github.com/ReactiveX/rxjs/issues/3733#issuecomment-391427430
With an observable you created yourself, you have access to the observer.error() method.
const obs = Observable.create(observer => {
// Emit a value.
observer.next("hello");
// Throw an error.
observer.error("my error");
});
Note: if you're using RxJS 4, you'll have to use onNext() and onError() instead.