RxJS Throw error via NodeEventHandler in fromEventPattern - javascript

If I am using fromEventPattern, and the custom API has a separate handler for errors, how can I throw them into the stream via the handler function (NodeEventHandler)?
import { fromEventPattern } from 'rxjs';
const token = someAPI.registerEventHandler(function(event) {}, function(error) {}); // API takes a second function for errors.
someAPI.unregisterEventHandler(token);
const someAPIObservable = fromEventPattern(
function(handler) { return someAPI.registerEventHandler(handler, handler); }, // What should be the second argument here, in order to throw the error to any subscribers
function(handler, token) { someAPI.unregisterEventHandler(token); }
);
I could call the handler with the error (as above) and then pipe the resulting observable returned from fromEventPattern in order to check the type but it seems a bit verbose:
pipe(
map((x: unknown) => {
if (x instanceof Error) throw x;
return x;
}),
subject (which I am trying to replace with fromEventPattern) has a specific .error() method. Which would avoid all of the manual piping and type checking.

try this wrapper out
const yourEventObservable=new Observable(obs=>{
const token=someAPI.registerEventHandler(res=>{
if (res instanceof Error)
obs.error(x);
obs.next(res)
})
return ()=>someAPI.unregisterEventHandler(token);
}).pipe(share())

Related

Angular response: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable [duplicate]

I am trying to map from a service call but getting an error.
Looked at subscribe is not defined in angular 2? and it said that in order to subscribe we need to return from inside the operators. I have return statements as well.
Here's my code:
checkLogin(): Observable<boolean> {
return this.service
.getData()
.map(
(response) => {
this.data = response;
this.checkservice = true;
return true;
},
(error) => {
// debugger;
this.router.navigate(["newpage"]);
console.log(error);
return false;
}
)
.catch((e) => {
return e;
});
}
Error log:
TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable
In my case the error occurred only during e2e tests. It was caused by throwError in my AuthenticationInterceptor.
I imported it from a wrong source because I used WebStorm's import feature. I am using RxJS 6.2.
Wrong:
import { throwError } from 'rxjs/internal/observable/throwError';
Correct:
import { throwError } from 'rxjs';
Here the full code of the interceptor:
import { Injectable } from '#angular/core';
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
#Injectable()
export class AuthenticationInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const reqWithCredentials = req.clone({withCredentials: true});
return next.handle(reqWithCredentials)
.pipe(
catchError(error => {
if (error.status === 401 || error.status === 403) {
// handle error
}
return throwError(error);
})
);
}
}
In your example code, you have your map operator receiving two callbacks, when it should only be receiving one. You can move your error handling code to your catch callback.
checkLogin():Observable<boolean>{
return this.service.getData()
.map(response => {
this.data = response;
this.checkservice=true;
return true;
})
.catch(error => {
this.router.navigate(['newpage']);
console.log(error);
return Observable.throw(error);
})
}
You'll need to also import the catch and throw operators.
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
EDIT:
Note that by returning Observable.throwin your catch handler, you won't actually capture the error - it will still surface to the console.
If your function is expecting to return a boolean, just do this:
Import:
import { of, Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
Then
checkLogin(): Observable<boolean> {
return this.service.getData()
.pipe(
map(response => {
this.data = response;
this.checkservice = true;
return true;
}),
catchError(error => {
this.router.navigate(['newpage']);
console.log(error);
return of(false);
})
)}
You are returning an Observable where as your code returns just a boolean. So you need to use as below
.map(response => <boolean>response.json())
If you are using another common service checkservice in your case, you can simply use
this.service.getData().subscribe(data=>console.log(data));
This will make your checkLogin() function with return type as void
checkLogin():void{
this.service.getData()
.map(response => {
this.data = response;
this.checkservice=true;
}).subscribe(data=>{ });
and you can use of this.checkService to check your condition
I've had this error when there's been different RxJS-versions across projects. The internal checks in RxJS fails because there are several different Symbol_observable. Eventually this function throws once called from a flattening operator like switchMap.
Try importing symbol-observable in some entry point.
// main index.ts
import 'symbol-observable';
I was forgetting to return the other observable in pipe(switchMap(
this.dataService.getPerson(personId).pipe(
switchMap(person => {
//this.dataService.getCompany(person.companyId); // return missing
return this.dataService.getCompany(person.companyId);
})
)
I had the same issue caused by importing the internal version of 'takeUntil' instead of the operators
Change
import { takeUntil } from 'rxjs/internal/operators/takeUntil';
to
import { takeUntil } from 'rxjs/operators';
This happen also for other operators
Can be triggered by a stray comma (,) in an RxJS pipe(...)
The compile won't catch this extra comma at the end:
pipe(first(), map(result => ({ event: 'completed', result: result}),);
It becomes an 'invisible' undefined operator which screws the whole pipe up, and leads to a very confusing error message - which in this case has nothing to do with my actual logic.
I have been facing this issue when trying to authenticate a user using JSON Web Token. in my case it's related to authentication interceptor.
Sending a request to authenticate a user doesn't have to provide a token since it doesn't exist yet.
Check that your interceptor include this:
if (req.headers.get('No-Auth') == "True")
return next.handle(req.clone());
And that you provide {'No-Auth':'True'} to your header's request like this:
authenticateUser(user): Observable<any> {
const headers = new HttpHeaders({'No-Auth':'True'});
headers.append('Content-Type', 'application/json');
return this.httpClient.post(`${this.apiEndpoint}/auth/authenticate`, user, {headers: headers});
}
A hint for anyone experiencing this. This can happen when a switchMap doesn't receive an observable return value (like null). Simply add a default case, so it always returns an observable.
switchMap((dateRange) => {
if (dateRange === 'Last 24 hours') {
return $observable1;
}
if (dateRange === 'Last 7 Days') {
return $observable2;
}
if (dateRange === 'Last 30 Days') {
return $observable3;
}
// This line will work for default cases
return $observableElse;
})
I wrote this because I arrive here searching for the same error, and this could be useful for someone in the future.
I get the same error while trying to initialize a service variable from its constructor making a call to a remote API trough http.get and .subscribe()
After many tests without understanding what the problem was, i finally get it:
My application had authentication and an HttpInterceptor, and i was trying to initialize the service calling a public API method using http.get(...) without 'No-Auth' headers. I added them like here, and problem solved for me:
getData() {
var reqHeader = new HttpHeaders({ 'Content-Type': 'application/x-www-urlencoded','No-Auth':'True' });
return this.http.get(environment.urlApi.Literales, { headers: reqHeader });
}
What a headache :(
In my case in Angular-5, service file was not imported from which i was accessing the method and subscribing the data.After importing service file it worked fine.
this error happened with me when i am using interceptor
you have to do this in your interceptor
return next.handle(request).map(event => {
if (event instanceof HttpResponse) {
}
return event;
},
catchError((error: HttpErrorResponse) => {
if (error.status === 401 || error.status === 400) {
// some logic
}
This error happened to me #angular 7
You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
The error is actually self-explanatory, it says somewhere in the observable I pass the invalid object. In my case, there was lots of API call but all the calls were failing because of wrong server configuration. I tried to use map, switchMap, or other rxjs operator but the operators are getting undefined objects.
So double-check your rxjs operator inputs.
I was also facing the same issue when i was calling a method inside switchMap, apparently I found that if we use method inside switchMap it must return observable.
i used pipe to return observable and map to perform operations inside pipe for an api call which i was doing inside method rather than subscribing to it.
I'm not sure if this will help anyone, but in my case further up my chain I was using distinctUntilChanged and an exception inside a function there was manifesting with this error message.
You will get the following error message too when you provide undefined or so to an operator which expects an Observable, eg. takeUntil.
TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable
In my case I mistakely imported Action into my combineEpics, rather than Epic...
Verify all the functions within combine Epics are epic funcitons
I had a similar error using RXJS in NESTJS.
Error: TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable. +1ms
In my case I forgot to return an Observable in a switchMap. This caused that no Observable was received in the next RXJS operator or client code.
Once I returned an Observable in the switchMap, the error disappeared.
I have the same exact error message while I was doing my unit test and throwing observable exception after mocking my services.
I resolved it by passing exact function and format inside Observable.throw.
Actual code which calls the service and subscribe to get data. notice that catch to handle the 400 error.
this.search(event).catch((e: Response) => {
if (e.status === 400) {
console.log(e.json().message);
} else if (e.url) {
console.log('HTTP Error: ' + e.status + ' ' + e.statusText,
'URL: ' + e.url, 'Info: ' + e.json().message));
}
}).finally(() => {
this.loading = false;
}).subscribe((bData) => {
this.data = bData;
});
The code inside the service
search() {
return this.someService.getData(request)
.do((r) => {
this.someService.defaultHeaders.delete('skipAlert');
return r;
})
.map((r) => {
return r.businessObjectDataElements.length && r.businessObjectDataElements || null;
});
}
Unit Testing
I have mocked the SomeService and returning observable data and its fine as it have all the required methods inside it.
someServiceApi = fixture.debugElement.injector.get(SomeService);
spyOn(someServiceApi, 'getData').and.returnValue(Observable.of({}));
The above code is okey but when when I was trying to test the catch/error condition by passing Observable.throw({}) it was showing me the error as it was expecting Response type return from the service.
So below service mocking return was giving me that error.
someServiceApi.getData
.and.returnValue(Observable.throw(new Response({status: 400, body: [], message: 'not found error'})));
So I Corrected it by replicating the exact expected function in my return object rather passing a Response type value.
someServiceApi.getData
.and.returnValue(Observable.throw({status: 400, json: () => { return {message: 'not found error'}}, body: []}));
// see `json: () => { return {message: 'not found error'}}` inside return value
In regard to the "You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable" error.
This could happen if you import { Observable } from 'rxjs' after (below) some module/function/whatever, which actually uses it.
Solution: move this import above the import of that module.

How to handle error and return observable while subscribe inside an observable function in Rxjs Angular

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: () => { }
});

JavaScript: differences between async error handling with async/await and then/catch

Just wanted to preemptively say that I am familiar with async/await and promises in JavaScript so no need to link me to some MDN pages for that.
I have a function to fetch user details and display it on the UI.
async function someHttpCall() {
throw 'someHttpCall error'
}
async function fetchUserDetails() {
throw 'fetchUserDetails error'
}
function displayUserDetails(userDetails) {
console.log('userDetails:', userDetails)
}
async function fetchUser() {
try {
const user = await someHttpCall()
try {
const details = await fetchUserDetails(user)
returndisplayUserDetails(details)
} catch (fetchUserDetailsError) {
console.log('fetching user error', fetchUserDetailsError)
}
} catch (someHttpCallError) {
console.log('networking error:', someHttpCallError)
}
}
It first makes HTTP call via someHttpCall and if it succeeds then it proceeds to fetchUserDetails and it that succeeds as well then we display the details on Ui via returndisplayUserDetails.
If someHttpCall failed, we will stop and not make fetchUserDetails call. In other words, we want to separate the error handling for someHttpCall and it’s data handling from fetchUserDetails
The function I wrote is with nested try catch blocks which doesn't scale well if the nesting becomes deep and I was trying to rewrite it for better readability using plain then and catch
This was my first atttempt
function fetchUser2() {
someHttpCall()
.then(
(user) => fetchUserDetails(user),
(someHttpCallError) => {
console.log('networking error:', someHttpCallError)
}
)
.then(
(details) => {
displayUserDetails(details)
}, //
(fetchUserDetailsError) => {
console.log('fetching user error', fetchUserDetailsError)
}
)
}
The problem with this is that the second then will run i.e. displayUserDetails even with someHttpCall failing. To avoid this I had to make the previous .catch blocks throw
so this is the updated version
function fetchUser2() {
someHttpCall()
.then(
(user) => fetchUserDetails(user),
(someHttpCallError) => {
console.log('networking error:', someHttpCallError)
throw someHttpCallError
}
)
.then(
(details) => {
displayUserDetails(details)
}, //
(fetchUserDetailsError) => {
console.log('fetching user error', fetchUserDetailsError)
}
)
}
However now the second catch will get called as a result of the throw. So when the someHttpCall failed, after we handled the someHttpCallError error, we would enter this block (fetchUserDetailsError) => { console.log('fetching user error', fetchUserDetailsError) } which is not good since fetchUserDetails never gets called so we shouldn't need to handle fetchUserDetailsError (I know someHttpCallError became fetchUserDetailsError in this case)
I can add some conditional checks in there to distinguish the two errors but it seems less ideal. So I am wondering how I can improve this by using .then and .catch to achieve the same goal here.
I am wondering how I can improve this by using .then and .catch to achieve the same goal here
You don't get to avoid the nesting if you want to replicate the same behaviour:
function fetchUser2() {
return someHttpCall().then(
(user) => {
return fetchUserDetails(user).then(
(details) => {
return displayUserDetails(details)
},
(fetchUserDetailsError) => {
console.log('fetching user error', fetchUserDetailsError)
}
)
},
(someHttpCallError) => {
console.log('networking error:', someHttpCallError)
throw someHttpCallError
}
)
}
(The exact equivalent to try/catch would use .then(…).catch(…) instead of .then(…, …), but you might not actually want that.)
The function I wrote is [nested] which doesn't scale well if the nesting becomes deep and I was trying to rewrite it for better readability […]
For that, I would recommend to combine await with .catch():
async function fetchUser() {
try {
const user = await someHttpCall().catch(someHttpCallError => {
throw new Error('networking error', {cause: someHttpCallError});
});
const details = await fetchUserDetails(user).catch(fetchUserDetailsError => {
throw new Error('fetching user error', {cause: fetchUserDetailsError});
});
return displayUserDetails(details);
} catch (someError) {
console.log(someError.message, someError.cause);
}
}
(The cause option for Error is still quite new, you might need a polyfill for that)
I can add some conditional checks in there to distinguish the two errors but it seems less ideal.
Actually, that sounds like an ideal situation. That means that you don't have to nest any try / catch blocks which could make you code a lot more readable. This is one of the things that async / await is meant to solve.
A solution could be is to create custom errors by extending the Error interface to be able to determine how and where the error occurs.
class CustomError extends Error {
constructor(name, ...args) {
super(...args)
this.name = name
}
}
Throw your errors within the functions that correspond with the error.
async function someHttpCall() {
throw new CustomError('HttpCallError', 'someHttpCall error');
}
async function fetchUserDetails(user) {
throw new CustomError('UserDetailsError', 'fetchUserDetails error')
}
Now you can control your error flow by checking the name property on the error to differentiate your errors.
async function fetchUser() {
try {
const user = await someHttpCall()
const details = await fetchUserDetails(user)
return displayUserDetails(details)
} catch (error) {
switch(error.name) {
case 'HttpCallError':
console.log('Networking error:', error)
break
case 'UserDetailsError':
console.log('Fetching user error', error)
break
}
}
}
I've been inspired by Rust's Result type (which forces you to handle every potential error along the way).
So what I do is handle exceptions in every individual function, and never allow one to throw, instead returning either an Error (if something went wrong) or the desired return value (if no exception occurred). Here's an example of how I do it (comments included):
TS Playground
If you aren't familiar with TypeScript, you can see the JavaScript-only version of the following code (with no type information) at the TypeScript Playground link above (on the right side of the page).
// This is the code in my exception-handling utility module:
// exception-utils.ts
export type Result <T = void, E extends Error = Error> = T | E;
export function getError (value: unknown): Error {
return value instanceof Error ? value : new Error(String(value));
}
export function isError <T>(value: T): value is T & Error {
return value instanceof Error;
}
export function assertNotError <T>(value: T): asserts value is Exclude<T, Error> {
if (value instanceof Error) throw value;
}
// This is how to use it:
// main.ts
import {assertNotError, getError, isError, type Result} from './exception-utils.ts';
/**
* Returns either Error or string ID,
* but won't throw because it catches exceptions internally
*/
declare function getStringFromAPI1 (): Promise<Result<string>>;
/**
* Requires ID from API1. Returns either Error or final number value,
* but won't throw because it catches exceptions internally
*/
declare function getNumberFromAPI2 (id: string): Promise<Result<number>>;
/**
* Create version of second function with no parameter required:
* Returns either Error or final number value,
* but won't throw because it catches exceptions internally
*
* The previous two functions work just like this, using the utilities
*/
async function fetchValueFromAPI2 (): Promise<Result<number>> {
try {
const id = await getStringFromAPI1(); // Error or string
assertNotError(id); // throws if `id` is an Error
return getNumberFromAPI2(id); // Error or number
}
catch (ex) {
return getError(ex);
}
}
async function doSomethingWithValueFromAPI2 (): Promise<void> {
const value = await fetchValueFromAPI2(); // value is number or Error
if (isError(value)) {
// handle error
}
else console.log(value); // value is number at this point
}

Ignore a rejected fire-and-forget promise in jest

My store's processAction() function calls a private async function in a fire-and-forget manner which then does a fetch. processAction() itself does not handle any error handling, and--in the browser--if the fetch fails, an external library handles any and all uncaught promise rejections.
So, if I mock my fetch to reject, the private function--the effects of which I am testing--will reject. Since I don't have a reference to the promise created by my async function call, I have no way of catching the rejection within the test, but the test fails because there was an unhandled rejection.
How can I tell jest to be okay with this short of calling the private function itself rather than just triggering the action that calls it?
actions.ts
const actions = {
doTheThing() {
dispatch({ type: 'DO_THE_THING' });
},
};
export default actions;
store.ts
import fetch from './fetch';
class Store {
isFetching = false;
// ...
processAction({ type, payload }: { type: string, payload: any }) {
switch (type) {
case 'DO_THE_THING':
this.fetchTheThing();
break;
}
}
private async fetchTheThing() {
try {
this.isFetching = true;
const result = await fetch(myUrl);
// ...
} finally {
this.isFetching = false;
}
}
}
export default new Store();
__mocks__/fetch.ts
let val: any;
interface fetch {
__setVal(value: any): void;
}
export default async function fetch() {
return val;
}
fetch.__setVal = function(value: any) {
val = value;
};
store.test.ts
import actions from './actions';
import store from './store';
const fetch = (require('./fetch') as import('./__mocks__/fetch')).default;
jest.mock('./fetch');
test('it sets/unsets isFetching on failure', async () => {
let rej: () => void;
fetch.__setVal(new Promise((_, reject) => rej = reject));
expect(store.isFetching).toBe(false);
Actions.doTheThing();
await Promise.sleep(); // helper function
expect(store.isFetching).toBe(true);
rej(); // <---- test fails here
await Promise.sleep();
expect(store.isFetching).toBe(false);
});
processAction is synchronous and unaware of promises and this results in a dangling promise. Dangling promises should never reject because this results in unhandled rejection, which is a kind of exception. This may cause an application to crash depending on the environment. Even if exceptions are handled globally, this shouldn't be an reason to not handle errors where they are expected.
A correct way to do this is to suppress a rejection explicitly either in fetchTheThing where it occurs:
private async fetchTheThing() {
try {
...
} catch {} finally {
this.isFetching = false;
}
}
Or in this case, it's more like processAction that results in dangling promise:
this.fetchTheThing().catch(() => {});
Otherwise unhandled rejection event is dispatched.
Without that, it could be tested by listening for the event:
...
let onRej = jest.fn();
process.once('unhandledRejection', onRej);
rej();
await Promise.sleep();
expect(onRej).toBeCalled();
expect(store.isFetching).toBe(false);
This won't work as intended if there's already another unhandledRejection listener, which can be expected in a good Jest setup. If this is the case, the only workaround that won't affect other tests is to reset them before the test and re-add afterwards:
let listeners;
beforeEach(() => {
listeners = process.rawListeners('unhandledRejection');
process.removeAllListeners('unhandledRejection');
});
afterEach(() => {
(typeof listeners === 'function' ? [listeners] : listeners).forEach(listener => {
process.on('unhandledRejection', listener);
});
})
This isn't recommended and should be used at own risk because this indicates a deeper problem with error handling that is not generally acceptable in properly designed JavaScript application.
My function calls a private async function in a fire-and-forget manner, and does not add any error handling.
Don't do that.
An external library handles any and all uncaught promise rejections. In production, I want the shell to handle it, so I do not want to handle it in the function itself.
Don't rely on this external library.
You should have your own global error handling function that you use in your function.
In production, have that error handling function simply rethrow the exception so that it gets picked up by the environment, or better, do call the shell error handling function directly if possible.
In the tests, you can mock out your own global handler, and assert that it is called with the expected arguments.

How can I catch an RxJS error only if it is unhandled

I am building an application using RxJS and one of the interesting problems I have come across is how I can implement an operator that will catch errors only when they are unhandled by the rest of the pipeline ahead of it. I'll call my operator catchUnhandledError for now.
The catchError operator is loosely similar (non stream) to
try {
// stream
} catch (err) {
// catchError handler
// new stream
}
What I am trying to implement resembles the following
try {
// stream
} catch (err) {
try {
// original stream
} catch (err2) {
// catchUnhandledError handler
// new stream
}
}
The key takeaway here is that the new operator only catches errors that other operators do not catch further down the pipeline.
I appreciate that piped operators essentially wrap observables similar to how middleware works in popular application pipelines which means "pushing the pipe to the end" is nonsensical.
My stream (simplified) is created as follows.
combineLatest([ page, filter ]).pipe(
switchMap(([ page, { search, order }]) =>
apiQuery(search, order, page).pipe(
map(/* combine response with filters */),
catchError(/* ONLY handle http status 422 Unprocessable Entity for validation */),
catchError(/* ONLY handle http status 409 Conflict for version conflicts *)
)
)
);
Where api.query returns:
function errorHandler(errorMessage?: string) {
return <T>(source: Observable<T>): Observable<T> =>
source.pipe(
/* replace with new catchUnhandledError operator */
catchError(() => {
/* More complex code than this to log the error */
alert('unhandled error');
return EMPTY;
})
);
}
function apiQuery(search: string, order: string, page: number) {
/* uses parameters to generate ajax payload */
return ajax({
url: 'url',
method: 'GET'
}).pipe(
map(r => r.response as T),
errorHandler('An error message')
);
}
The problem is the validation / request specific error handlers never get called because the generic errorHandler takes precedence. A couple of work arounds I will have to use if this is not possible:
Pass everything in the pipe as a parameter / callback (convoluted)
Map success and errors to a single object then check .success (convoluted)
Copy and paste the generic errorHandler to every single place I call my api (duplicated)
Does anyone have any ideas that will prevent me from having to have convoluted or duplicated code?
The first thing that comes to mind is to create a custom operator that will be given a callback function with which you can determine whether the catchError should handle this or should pass it along further in the stream.
const catchUnhandledError = (shouldHandle: (err: any) => boolean) =>
(source: Observable<any>) => defer(
() => source.pipe(
catchError(err => shouldHandle(err) ? of('handled') : throwError(err) /* pass along the error */)
)
)

Categories

Resources