calling subscribe on observable inside try catch block - rxjs

Can someone explain to me why this is not getting caught by the catch block, I am thinking its because I have a switch map, which would unsubscribe from the first observable. Have commented it out with no luck. The first observable (this.apiPaymentService.deleteStoredPaymentMethod()) is the one causing a 403
try {
this.apiPaymentService.deleteStoredPaymentMethod()
.pipe(
switchMap(() => this.apiPaymentService.getStoredAchPaymentMethods(),
catchError((err) => { return throwError(err)})
).subscribe()
} catch (error) {
console.log('I am never called ')
this.errorModal(error);
}

First: Make sure your code parses
If I throw this into my IDE, I get a parsing error immediately. I'll assume you don't want an operator as a second parameter to switchMap and that you've simply forgotten an ) near the end of the line.
What's happening inside your try block?
Inside your try block, you subscribe to an observable. That's it. That's what happens there. The subscription happens without an error. So your try-catch will complete without an error.
If this.apiPaymentService.getStoredAchPaymentMethods() is asyncronous, the try block will have completed (and may not even be on the call stack anymore) before getStoredAchPaymentMethods() resolves with a value.
How to manage Asynchronous Errors
You're already most of the way there! Instead of catching and then rethrowing an error, you can manage it right there.
The following is a no-op:
catchError((err) => { return throwError(err)})
But you can return something else instead, or you can retry or something like that :)
Aside: not just observables
Here's some old-skool code with the same underlying phenomenon.
function throwErrorFunction() {
console.log("Throwing an error!");
throw 'Throwing an error from my throw error function';
}
try {
setTimeout(throwErrorFunction, 5000);
} catch (error) {
console.log('I am never called ');
}
Here's a way to think about this. The code above never calls the throwErrorFunction. Function application in JavaScript is done wiht parenthesis. So you'd need something like throwErrorFunction() somewhere in order to call the function. But that never happens here.
But somehow the console will still read "Throwing an error!". So how does that happen if we've not called throwErrorFunction? Well... setTimeout eventually calls throwErrorFunction for us! In this case, it waits 5 seconds and then calls the function.
But by the time five seconds have elapsed, the try-catch statement has already been executed. Same deal as before! :)

Related

rxjs interval will execute if another 2 observables are true

I'm writing an angular15 app with a youtube player component in it, i'm trying to work with rxjs but i think that i have one issue that i got wrong, the mergeMap. i'm really new to rxjs so sorry for any mistakes
I have 2 subscriptions, one for if youtube library as finished loading, and the other if the youtube player is ready.
first lets look just at the interval:
this.YTSubscription=interval(100).pipe(
exhaustMap((x, y)=>{
this.currentTimeSubject.next(this.player.getCurrentTime());
this.isPlayingSubject.next(this.player.getPlayerState() === YT.PlayerState.PLAYING);
this.isMutedSubject.next(this.player.isMuted());
this.volumeSubject.next(this.player.getVolume());
return of(true);
}),
).subscribe({next: (data )=>{
},
error: (err)=> {
this.YTSubscription?.unsubscribe();
}
});
this works fine, it runs in intervals on 100ms and i use exhaustMap to make sure that the next iteration will be executed only if the previous one completed in case when i'll add more calculations it may take more than 100 ms.
next i want in the interval to check if youtube is loaded, for that i have the observable isYouTubeLoaded, so i tried using mergeMap for this.. i guess this is not the right way? but it still worked:
this.YTSubscription=interval(100).pipe(
mergeMap(x => this.isYouTubeLoaded),
exhaustMap((x, y)=>{
if (!x) {
return of(false);
}
...
now x inside exahustMap contains the isYouTubeLoaded and this does the job.
now i have another observable that i want to check and only if both of them are true to run the interval, if not to wait for the next iteration, this is where i get lost because if i add another mergeMap i can't see both values in exhaustMap.
so from reading some more i assume that i'm not supposed to use mergeMap at all, maybe filter ? but i still have no clue how to do that with 2 observables.
any ideas?
I'm not entirely sure, what you want to do, but I'll try to answer this part of your question:
now i have another observable that i want to check and only if both of them are true to run the interval, if not to wait for the next iteration, this is where i get lost because if i add another mergeMap i can't see both values in exhaustMap.
combineLatest([src1, src2]).pipe( // check both
filter(([ok1, ok2]) => ok1 && ok2), // only if both are true
switchMap(() => timer(...) // run the timer
).subscribe(...);
#churill really helped, in the end i need two pipes and not 3 but the implementation is the same, still marking his answer as the correct one, just showing here the resulting code:
this.YTSubscription=combineLatest([interval(100), this.isYouTubeLoaded]).pipe(
map(([intr, loaded])=>(loaded)),
filter((loaded)=> (loaded)),
exhaustMap(()=>{
try {
if (this.player.getPlayerState() === YT.PlayerState.UNSTARTED) {
return of(false);
}
} catch (e) {
return of(false);
}
this.currentTimeSubject.next(this.player.getCurrentTime());
this.isPlayingSubject.next(this.player.getPlayerState() === YT.PlayerState.PLAYING);
this.isMutedSubject.next(this.player.isMuted());
this.volumeSubject.next(this.player.getVolume());
return of(true);
}),
).subscribe({next: (isUpdated)=>{
},
error: (err)=> {
console.error(err);
}
});

Returning a callback and calling a callback inside a promise chain in AJAX request

I was working on a react project and the following three cases came up. Can someone give me some pointers as to what the differences are when making AJAX request using axios as well as redux-promise?
Case 1 (the payload is undefined - why?):
axios.get(link)
.then(callback)
Case 2 (the payload is also undefined - why?):
axios.get(link)
.then(() => callback())
Case 3 (the payload is the res object):
axios.get(link)
.then((res) => { callback; return res });
Would really appreciate if anyone could please answer this question or give me pointers to materials that could clear my confusion. I did my best researching and have spent couple hours before I posted the question on SO. Thanks!
Your resolved value in the first two promises is undefined because you have a .then() handler that you don't return anything from. The return value of the .then() handler becomes the resolved value of the promise chain.
If you want link to be the resolved value of the promise and you're going to have your own .then() handler, then you have to return it.
Case 1:
axios.get(link).then(callback)
Here you're delegating your .then() handler to the callback function. Unless it returns the value it was passed to it (which apparently it doesn't), then the return value from your .then() handler is undefined and thus that's what the resolved value of the promise chain becomes.
Case 1 is equivalent to this:
axios.get(link).then(function(val) {
return callback(val);
});
So, the promise chain takes on whatever callback(val) returns as the resolved value.
Case 2:
axios.get(link).then(() => callback())
Here, we can clearly see that the return value from the .then() arrow function handler is the result of executing callback() which is apparently undefined so thus the promise chain takes on a resolved value of undefined.
You could fix that by doing this:
axios.get(link).then(() => {
callback()
return link;
});
Case 3:
axios.get(link).then((res) => { callback; return res });
Here's you're explicitly returning res so that becomes the resolved value of the promise chain. You're also not even calling callback(). I presume you meant to have () after it.
Don't mix plain callbacks in with promises
You also probably don't want to mix plain callbacks with promises and they are two different approaches to asynchronous notification and it's much better to use only one technique in a given section of code. Instead, turn the callback operation into one that returns a promise that is resolved when the callback would normally get called. Then, you can just chain promises.
Or, if you're trying to notify some caller about the completion of the operation, then have them just watch the returned promise with their own .then() handler on the returned promise.

RXJS repeat does not have a chance to repeat?

I have the following epic I use in my application to handle api requests:
action$ => {
return action$.ofType(actions.requestType)
.do(() => console.log('handled epic ' + actions.requestType))
.switchMap((action) => (
Observable.create((obs) => {
obs.next({ type: type, value: action.value, form: action.form });
})
.debounceTime(250)
.switchMap((iea) => (
Observable.ajax(ajaxPost(url(iea.value), body ? body(iea.value) : action.form))
.mergeMap(payload => {
return Observable.merge(
Observable.of(actions.success(payload)),
/* some other stuff */
);
})
.catch(payload => {
return [actions.failure(payload)];
})
))
))
.takeUntil(action$.filter((a) => (a.type === masterCancelAction))
.repeat();
};
Basically, any time I perform an api request, I dispatch a request action. If I dispatch another request quickly, the previous one is ignored using debounceTime. Additionally, the request can be cancelled using the masterCancelAction and when cancelled repeat() restarts the epic. This epic works as intended in all cases expect one.
The failure case occurs when a user uses the browser back during a request. In this case I fire the masterCancelAction to the request. However, on the same execution context as a result from the masterCancelAction, another request action dispatches to perform a new request on the same epic, but the api request does not occur (the console.log does occur though) as if there was no repeat(). In other cases where cancels occur, the next request is not invoked from the same execution context and it works fine, so it seems in this case my code does not give repeat a chance to restart the epic?
A dirty workaround I found was to use setTimeout(dispatch(action), 0) on the request that dispatches after the cancellation. This seems to allow repeat() to execute. I tried passing different schedulers into repeat, but that didn't seem to help. Also, attaching takeUntil and repeat into my inner switchMap solves the problem, but then other cases where my next request does not execute in the same call stack fail.
Is there a way I can solve this problem without using setTimeout? Maybe it is not a repeat related problem, but it seems to be the case.
Using rxjs 5.0.3 and redux-observable 0.14.1.
The issue is not 100% clear without something like a jsbin to see what you mean, but I do see some general issues that might help:
Anonymous Observable never completes
When creating a custom anonymous Observable it's important to call observer.complete() if you do indeed want it to complete. In most cases, not doing so will cause the subscription to be a memory leak and might also other strange behaviors
Observable.create((observer) => {
observer.next({ type: type, value: action.value, form: action.form });
observer.complete();
})
Observable.of would have been equivalent:
Observable.of({ type: type, value: action.value, form: action.form })
However, it's not clear why this was done as the values it emits are in captured in scope.
debounceTime in this case does not debounce, it delays
Since the anonymous observable it's applied to only ever emits a single item, debounceTime will act just as a regular .delay(250). I'm betting you intended instead to debounce actions.requestType actions, in which case you'd need to apply your debouncing outside the switchMap, after the action$.ofType(actions.requestType).
Observable.of accepts any number of arguments to emit
This is more of a "did you know?" rather than an issue, but I noticed you're merging your of and /* some other actions */ I assume would be other of observables merged in. Instead, you can just return a single of and pass the actions as arguments.
Observable.of(
actions.success(payload),
/* some other actions */
actions.someOtherOne(),
actions.etc()
);
Also, when you find yourself emitting multiple actions synchronously like this, consider whether your reducers should be listening for the same, single action instead of having two or more. Sometimes this wouldn't make sense as you want them to have completely unrelated actions, just something to keep in mind that people often forget--that all reducers receive all actions and so multiple reducers can change their state from the same action.
.takeUntil will stop the epic from listening for future actions
Placing the takeUntil on the top-level observable chain causes the epic to stop listening for action$.ofType(actions.requestType), which is why you added the .repeat() after. This might work in some cases, but it's inefficient and can cause other hard to realize bugs. Epics should be thought of instead as sort of like sidecar processes that usually "start up" with the app and then continue listening for a particular action until the app "shuts down" aka the user leaves the app. They aren't actually processes, it's just helpful to conceptually think of them this way as an abstraction.
So each time it matches its particular action it then most often will switchMap, mergeMap, concatMap, or exhaustMap into some side effect, like an ajax call. That inner observable chain is what you want to make cancellable. So you'd place your .takeUntil on it, at the appropriate place in the chain.
Summary
As mentioned, it's not clear what you intended to do and what the issue is, without a more complete example like a jsbin. But strictly based on the code provided, this is my guesstimate:
const someRequestEpic = action$ => {
return action$.ofType(actions.requestType)
.debounceTime(250)
.do(() => console.log('handled epic ' + actions.requestType))
.switchMap((action) =>
Observable.ajax(ajaxPost(url(action.value), body ? body(action.value) : action.form))
.takeUntil(action$.ofType(masterCancelAction))
.mergeMap(payload => {
return Observable.of(
actions.success(payload),
/* some other actions */
...etc
);
})
.catch(payload => Observable.of(
actions.failure(payload)
))
);
};
Check out the Cancellation page in the redux-observable docs.
If this is a bit confusing, I'd recommend digging a bit deeper into what Observables are and what an "operator" is and does so that it doesn't feel magical and where you should place an operator makes more sense.
Ben's post on Learning Observable by Building Observable is a good start.

Pattern for Observables that includes acknowledgement

I'm working on something that is recording data coming from a queue. It was easy enough to process the queue into an Observable so that I can have multiple endpoints in my code receiving the information in the queue.
Furthermore, I can be sure that the information arrives in order. That bit works nicely as well since the Observables ensure that. But, one tricky bit is that I don't want the Observer to be notified of the next thing until it has completed processing the previous thing. But the processing done by the Observer is asynchronous.
As a more concrete example that is probably simple enough to follow. Imagine my queue contains URLs. I'm exposing those as an Observable in my code. The I subscribe an Observer whose job is to fetch the URLs and write the content to disk (this is a contrived example, so don't take issue with these specifics). The important point is that fetching and saving are async. My problem is that I don't want the observer to be given the "next" URL from the Observable until they have completed the previous processing.
But the call to next on the Observer interface returns void. So there is no way for the Observer to communicate back to me that has actually completed the async task.
Any suggestions? I suspect there is probably some kind of operator that could be coded up that would basically withhold future values (queue them up in memory?) until it somehow knew the Observer was ready for it. But I was hoping something like that already existed following some established pattern.
similar use case i ran into before
window.document.onkeydown=(e)=>{
return false
}
let count=0;
let asyncTask=(name,time)=>{
time=time || 2000
return Rx.Observable.create(function(obs) {
setTimeout(function() {
count++
obs.next('task:'+name+count);
console.log('Task:',count ,' ', time, 'task complete')
obs.complete();
}, time);
});
}
let subject=new Rx.Subject()
let queueExec$=new Rx.Subject()
Rx.Observable.fromEvent(btnA, 'click').subscribe(()=>{
queueExec$.next(asyncTask('A',4000))
})
Rx.Observable.fromEvent(btnB, 'click').subscribe(()=>{
queueExec$.next(asyncTask('B',4000))
})
Rx.Observable.fromEvent(btnC, 'click').subscribe(()=>{
queueExec$.next(asyncTask('C',4000))
})
queueExec$.concatMap(value=>value)
.subscribe(function(data) {
console.log('onNext', data);
},
function(error) {
console.log('onError', error);
},function(){
console.log('completed')
});
What you describe sounds like "backpressure". You can read about it in RxJS 4 documentation https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/backpressure.md. However this is mentioning operators that don't exist in RxJS 5. For example have a look at "Controlled Observables" that should refer to what you need.
I think you could achieve the same with concatMap and an instance of Subject:
const asyncOperationEnd = new Subject();
source.concatMap(val => asyncOperationEnd
.mapTo(void 0)
.startWith(val)
.take(2) // that's `val` and the `void 0` that ends this inner Observable
)
.filter(Boolean) // Always ignore `void 0`
.subscribe(val => {
// do some async operation...
// call `asyncOperationEnd.next()` and let `concatMap` process another value
});
Fro your description it actually seems like the "observer" you're mentioning works like Subject so it would make maybe more sense to make a custom Subject class that you could use in any Observable chain.
Isn't this just concatMap?
// Requests are coming in a stream, with small intervals or without any.
const requests=Rx.Observable.of(2,1,16,8,16)
.concatMap(v=>Rx.Observable.timer(1000).mapTo(v));
// Fetch, it takes some time.
function fetch(query){
return Rx.Observable.timer(100*query)
.mapTo('!'+query).startWith('?'+query);
}
requests.concatMap(q=>fetch(q));
https://rxviz.com/v/Mog1rmGJ
If you want to allow multiple fetches simultaneously, use mergeMap with concurrency parameter.

Q Promises - Create a dynamic promise chain then trigger it

I am wondering if there's a way to create a promise chain that I can build based on a series of if statements and somehow trigger it at the end. For example:
// Get response from some call
callback = (response) {
var chain = Q(response.userData)
if (!response.connected) {
chain = chain.then(connectUser)
}
if (!response.exists) {
chain = chain.then(addUser)
}
// etc...
// Finally somehow trigger the chain
chain.trigger().then(successCallback, failCallback)
}
A promise represents an operation that has already started. You can't trigger() a promise chain, since the promise chain is already running.
While you can get around this by creating a deferred and then queuing around it and eventually resolving it later - this is not optimal. If you drop the .trigger from the last line though, I suspect your task will work as expected - the only difference is that it will queue the operations and start them rather than wait:
var q = Q();
if(false){
q = q.then(function(el){ return Q.delay(1000,"Hello");
} else {
q = q.then(function(el){ return Q.delay(1000,"Hi");
}
q.then(function(res){
console.log(res); // logs "Hi"
});
The key points here are:
A promise represents an already started operation.
You can append .then handlers to a promise even after it resolved and it will still execute predictably.
Good luck, and happy coding
As Benjamin says ...
... but you might also like to consider something slightly different. Try turning the code inside-out; build the then chain unconditionally and perform the tests inside the .then() callbacks.
function foo(response) {
return = Q().then(function() {
return (response.connected) ? null : connectUser(response.userData);
}).then(function() {
return (response.exists) ? null : addUser(response.userData);//assuming addUser() accepts response.userData
});
}
I think you will get away with returning nulls - if null doesn't work, then try Q() (in two places).
If my assumption about what is passed to addUser() is correct, then you don't need to worry about passing data down the chain - response remains available in the closure formed by the outer function. If this assumption is incorrect, then no worries - simply arrange for connectUser to return whatever is necessary and pick it up in the second .then.
I would regard this approach to be more elegant than conditional chain building, even though it is less efficient. That said, you are unlikely ever to notice the difference.

Resources