Observable Finally on Subscribe - javascript

According to this artcle, onComplete and onError function of the subscribe are mutually exclusive.
Meaning either onError or onComplete events will fire up in my subscribe.
I have a logic block which needs to be executed whether I receive an error, or I finish my steam of information successfully.
I looked up for something like finally in python, but all I found is finally which needs to be attached to the observable I create.
But I want to to do that logic only when I subscribe, and after the stream has ended, whether successfully or with an error.
Any ideas?

The current "pipable" variant of this operator is called finalize() (since RxJS 6). The older and now deprecated "patch" operator was called finally() (until RxJS 5.5).
I think finalize() operator is actually correct. You say:
do that logic only when I subscribe, and after the stream has ended
which is not a problem I think. You can have a single source and use finalize() before subscribing to it if you want. This way you're not required to always use finalize():
let source = new Observable(observer => {
observer.next(1);
observer.error('error message');
observer.next(3);
observer.complete();
}).pipe(
publish(),
);
source.pipe(
finalize(() => console.log('Finally callback')),
).subscribe(
value => console.log('#1 Next:', value),
error => console.log('#1 Error:', error),
() => console.log('#1 Complete')
);
source.subscribe(
value => console.log('#2 Next:', value),
error => console.log('#2 Error:', error),
() => console.log('#2 Complete')
);
source.connect();
This prints to console:
#1 Next: 1
#2 Next: 1
#1 Error: error message
Finally callback
#2 Error: error message
Jan 2019: Updated for RxJS 6

The only thing which worked for me is this
fetchData()
.subscribe(
(data) => {
//Called when success
},
(error) => {
//Called when error
}
).add(() => {
//Called when operation is complete (both success and error)
});

I'm now using RxJS 5.5.7 in an Angular application and using finalize operator has a weird behavior for my use case since is fired before success or error callbacks.
Simple example:
// Simulate an AJAX callback...
of(null)
.pipe(
delay(2000),
finalize(() => {
// Do some work after complete...
console.log('Finalize method executed before "Data available" (or error thrown)');
})
)
.subscribe(
response => {
console.log('Data available.');
},
err => {
console.error(err);
}
);
I have had to use the add medhod in the subscription to accomplish what I want. Basically a finally callback after the success or error callbacks are done. Like a try..catch..finally block or Promise.finally method.
Simple example:
// Simulate an AJAX callback...
of(null)
.pipe(
delay(2000)
)
.subscribe(
response => {
console.log('Data available.');
},
err => {
console.error(err);
}
)
.add(() => {
// Do some work after complete...
console.log('At this point the success or error callbacks has been completed.');
});

Related

How do I make a new call to an api when a previous one finished successfully?

I am new to angular and rxjs, and I have the following scenario, in which I need that after a call to an api is successfully resolved to make a new call, in the context of angular / rxjs I don't know how to do it
handler(): void {
this.serviceNAme
.createDirectory(this.path)
.pipe(
finalize(() => {
this.someProperty = false;
})
)
.subscribe(
(data) => console.log(data),
(error) => console.error(error.message)
);
}
What is the correct way to make a new call to an api when a previous one was successful?
I understand you have a serviceOne and a serviceTwo. And you want to call serviceTwo using the retrieved data from serviceOne.
Using rxjs switchMap you can pipe one observable into another.
handler(): void {
this.serviceOne
.createDirectory(this.path)
.pipe(
switchMap(serviceOneResult => {
// transform data as you wish
return this.serviceTwo.methodCall(serviceOneResult);
})
)
.subscribe({
next: serviceTwoResult => {
// here we have the data returned by serviceTwo
},
error: err => {},
});
}
If you don't need to pass the data from serviceOne to serviceTwo but you need them to be both completed together, you could use rxjs forkJoin.
handler(): void {
forkJoin([
this.serviceOne.createDirectory(this.path),
this.serviceTwo.methodCall()
])
.subscribe({
next: ([serviceOneResult, serviceTwoResult]) => {
// here we have data returned by both services
},
error: err => {},
});
}
Using aysnc and await you can do:
async handler(): void {
await this.serviceNAme
.createDirectory(this.path)
.pipe(
finalize(() => {
this.someProperty = false;
})
)
.subscribe(
(data) => console.log(data),
(error) => console.error(error.message)
);
// Do second api call
}
There are a few says to do this:
Scenario # 1
Your two service api calls are independent, you just want one to go and then the next
const serviceCall1 = this.serviceName.createDirectory(this.path);
const serviceCall2 = this.serviceName.createDirectory(this.otherPath);
concat(serviceCall1 , serviceCall2).subscribe({
next: console.log,
error: err => console.error(err.message),
complete: () => console.log("service call 1&2 complete")
});
Scenario # 2
Your two calls dependant on one another, so you need the result of the first before you can start the second
this.serviceName.getDirectoryRoot().pipe(
switchMap(root => this.serviceName.createDirectoryInRoot(root, this.path))
).subscribe({
next: console.log,
error: err => console.error(err.message),
complete: () => console.log("service call 1 used to create service call 2, which is complete")
});
You'll want scenario # 2, because done this way, an error in the first call will mean no result is sent to the switchMap, and second call is never made.

Cannot get result of first HTTP request using RxJS' switchMap

I am trying to make 2 HTTP requests and in the first call I try to create a record and then according to its results (response from the API method) I want to execute or omit the second call that updates another data. However, although I can catch the error in catchError block, I cannot get the response in the switchMap method of the first call. So, what is wrong with this implementation according to teh given scenario? And how can I get the response of the first result and continue or not to the second call according to this first response?
let result;
let statusCode;
this.demoService.create(...).pipe(
catchError((err: any) => { ... }),
switchMap(response => {
// I am trying to get the response of first request at here
statusCode = response.statusCode;
if(...){
return this.demoService.update(...).pipe(
catchError((err: any) => { ... }),
map(response => {
return {
result: response
}
}
)
)}
}
))
.subscribe(result => console.log(result));
The question is still vague to me. I'll post a more generic answer to make few things clear
There are multiple things to note
When an observable emits an error notification, the observable is considered closed (unless triggered again) and none of the following operators that depend on next notifications will be triggered. If you wish to catch the error notifications inside the switchMap, you could return a next notification from the catchError. Something like catchError(error => of(error)) using RxJS of function. The notification would then be caught by the following switchMap.
You must return an observable from switchMap regardless of your condition. In this case if you do not wish to return anything when the condition fails, you could return RxJS NEVER. If you however wish to emit a message that could be caught by the subscriptions next callback, you could use RxJS of function. Replace return NEVER with return of('Some message that will be emitted to subscription's next callback');
import { of, NEVER } from 'rxjs';
import { switchMap, catchError, map } from 'rxjs/operators';
this.demoService.create(...).pipe(
catchError((err: any) => { ... }),
switchMap(response => {
statusCode = response.statusCode;
if (someCondition) {
return this.demoService.update(...).pipe( // emit `update()` when `someCondition` passes
catchError((err: any) => { ... }),
map(response => ({ result: response }))
);
}
// Show error message
return NEVER; // never emit when `someCondition` fails
}
)).subscribe({
next: result => console.log(result),
error: error => console.log(error)
});
You can implement with iif
this.demoService
.create(...)
.pipe(
// tap first to be sure there's actually a response to process through
tap(console.log),
// You can use any condition in your iif, "response.user.exists" is just a sample
// If the condition is true, it will run the run the update$ observable
// If not, it will run the default$
// NOTE: All of them must be an observable since you are inside the switchMap
switchMap(response =>
iif(() =>
response.user.exists,
this.demoService.update(response.id), // Pass ID
of('Default Random Message')
)
),
catchError((err: any) => { ... })
);

RxJS: Run asynchronous code before next or error

I have the following code:
of(true)
.pipe(
take(1)
)
.subscribe(async (_) => {
await this.close();
await Promise.all(...);
}, _ => {
this.close();
console.error(_);
});
I want to only write once this.close(), however I can't use finalize since it has to be run before Promise.all. Is there something I could use to run a code (and wait for it to resolve) before next or error ?
Is there something I could use to run a code (and wait for it to resolve) before next or error
I think materialize() and dematerialize() can be useful here:
src$.pipe(
// Suppress errors so that `delayWhen()` can run properly
// by converting everything into `Notification` objects
materialize(),
// Wait until `close()` finishes
delayWhen(() => this.close())
// Convert from `Notification` to the initial state: `next` || `error` || `complete`
// so that their handlers can be invoked accordingly
dematerialize(),
)
Note that with this approach it is going to wait for a complete notification as well, which can be avoided with the help of filter().
You could emit a value on error with catchError. This way your next callback will be executed when an error occurs.
const ERROR_TOKEN = 'my-error';
of(true).pipe(
take(1),
catchError(_ => {
console.error(_);
return of(ERROR_TOKEN); // emit some kind of error value
})
)
.subscribe(async (value) => {
// do on normal and error value
await this.close();
if (value !== ERROR_TOKEN) {
// only do on normal values
await Promise.all(...);
}
});
Or write a custom operator that merges your async code into the stream.
function awaitOnNextAndError<T, R extends ObservableInput<any>>(
asyncOnNextAndError: () => R,
sideEffectOnError?: (e) => void
): MonoTypeOperatorFunction<T> {
// only run your async code on 'subscribe' and ignore its output
const pre = defer(() => asyncOnNextAndError()).pipe(ignoreElements())
return (source: Observable<T>) => source.pipe(
// do some side effect on errors immediately, e.g. logging
tap({ error: sideEffectOnError }),
// execute your async code before you continue with the value from the stream
mergeMap(v => concat(pre, of(v))),
// execute your async code on error
catchError(e => pre)
)
}
of(true).pipe(
take(1),
awaitOnNextAndError(() => this.close(), console.error)
)
.subscribe(async (_) => {
await Promise.all(...);
});
https://stackblitz.com/edit/rxjs-efscr8

Catch in Observable stops HTTP Calls from Observable.interval

Observable.interval(10000)
.switchMap(() => this.http.get(url))
.catch (err => Observable.empty())
.subscribe(data => render(data))
Each 10 seconds we make an HTTP call. If an error happens, observable becomes completed, it doesn't make any calls anymore. How to prevent that?
That's correct behavior, when a complete or error notification is sent observers unsubscribe and the chain is disposed.
You can use the retry() operator to resubscribe but it's hard to tell what is your goal from this brief description.
Observable.interval(10000)
.switchMap(() => this.http.get(url))
.retry()
.subscribe(data => render(data))
takeUntil() of observable.
RxJS implements the takeUntil operator. You can pass it either an Observable or a Promise that it will monitor for an item that triggers takeUntil to stop mirroring the source Observable.
for more info click here
Try this:
let dataX = Observable.interval(10000)
.switchMap(() => this.http.get(url));
let caught = dataX.catch(
Observable.return({
error: 'There was an error in http request'
}))
caught.subscribe((data) => { return render(data) },
// Because we catch errors now, `error` will not be executed
(error) => {console.log('error', error.message)}
)
if you want you can put any condition when the error comes like
if(!data[error]){
render(data)
}
I hope that it helps you
Observable.interval(10000)
.switchMap(() => this.http.get(url)
.map(res => res.json())
.catch (err => Observable.empty()))
.subscribe(data => render(data))

Simplify a promise

How to simplify this statement:
Foo.execSomething(k,v).then(function(){/*do nothing*/},(err) => {
console.log(err)
}).catch((err) => {
console.log(err)
});
I call those suffix (starting from .then) a lot, I don't care if it's succeed, but I care when it's failed, is there simpler way to do it?
How about just using the catch function?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
The catch() method returns a Promise and deals with rejected cases only. It behaves the same as calling Promise.prototype.then(undefined, onRejected).
The then function in a Promise Object has to parameters, the first parameter is to handle when success, and the second is to handle error. But it's not suggest to catch error in then method.
Those two are same(I use console.log as error handler):
// first way:
Promise.reject('error').then(success => {}, err => console.log(err));
// console logs "error"
// second way
Promise.reject('error').catch(err => console.log(err));
// console logs "error"
// your code
Promise.reject('error').then(success => {}, err => console.log(err))
.catch(err => console.log(err))
// it will prints only a "error" in the console, so there are the same
so, if you do not care when it succeed, you could simplify your code like this:
Foo.execSomething(k,v).catch(err => console.log)

Categories