Software engineer, I've been working with Angular for 6 years now and strive to work more full stack. I publish on dev.to about topics I love around open source, frontend, data privacy, automations.
const source$ = new Observable(); // Not best practice
I think you should clarify your thinking here because doing so can definitely be legit to wrap resources into observables and cleanup once the stream is erroring or complete. For example you could use this to create a cold observable which would add some 3D shape on a scene in webgl and whenever you unsubscribe from that observable you would run the code to remove it from the scene. Dealing with such an observable in a switchmap for example will make your life way easier and your code cleaner by encapsulating the creation and the removal of 3rd party objects/data without having to deal with both outside of this observable within some tap where you have to remember to do so
Thank you for your comments. Could you provide a specific code example? I had understood the guidance to be to use a creation function instead of new Observable() where possible. And that we should only use new when creating a Subject/BehaviorSubject. But I'm happy to correct if my information is not accurate. Thanks!
Software engineer, I've been working with Angular for 6 years now and strive to work more full stack. I publish on dev.to about topics I love around open source, frontend, data privacy, automations.
Now the code above will open a toast (and as soon as the default timeout is reached it'll be closed).
Let say that now, we have an HTTP call which may take super long, so we want the user to be aware of the status.
You could do the following:
import{ToastrService}from'ngx-toastr';import{tap}from'rxjs/operators';@Component({// ...})exportclassYourComponent{publicres;constructor(privatehttp:HttpClient,privatetoastr:ToastrService){consttoastRef=this.toastr.info(`Please wait as it may take a moment`,`Some work is being triggered`,{// the call may take longer than the toast timeout in this case// so we turn the timeout offdisableTimeOut:true,}).toastRef;this.http.get(// some request that'll take long`http://my-api.com`).pipe(tap((res)=>{this.res=res;this.toastRef.close();}),catchError(()=>{this.toastRef.close();})).subscribe();}}
What you could do instead would be something like this:
import{ToastrService}from'ngx-toastr';import{Observable}from'rxjs';import{take,exhaustMap,shareReplay}from'rxjs/operators';@Component({// ...})exportclassYourComponent{privatetoastPleaseWait$:Observable<void>=newObservable<void>((observer)=>{consttoastReference=this.toastr.info(`Please wait as it may take a moment`,`Some work is being triggered`,{// the call may take longer than the toast timeout in this case// so we turn the timeout offdisableTimeOut:true,});observer.next();// this callback will be closed whenever the stream is closed// and this is where we can perform some cleanup, in this case// closing the toastreturn()=>toastReference.toastRef.close();});// subscribe in the view with async to display the resultpublicres$=this.toastPleaseWait$.pipe(exhaustMap(()=>this.http.get(// some request that'll take long`http://my-api.com`)),// make sure we close the stream so that toast$ is also// getting closed as soon as we've got our result and// therefore will run its cleanup callbacktake(1));constructor(privatehttp:HttpClient,privatetoastr:ToastrService){}}
The nice about this approach IMO is that the 3rd party resource manipulation which doesn't have a reactive public API is wrapped into a cold observable (which we could subscribe to multiple times) and no one down the stream has to worry about what was created and requires to be cleaned up.
It would also let us take advantage of Rxjs further if for example we were using a retry on the HTTP call. If one HTTP call was to start: the popup would show. As soon as it's completed or throws, the popup would be closed. If we do add retry(3) at the end of our stream, and the call was failing the first 2 times then succeeding the third one, we'd see:
popup opening
(fail) popup closing
(automatic retry) popup opening
(fail) popup closing
(automatic retry) popup opening
(success) popup closing
In conclusion, I'd say that when you have to work with APIs that are not exposing some reactive bindings you can isolate the creation and teardown logic of those by using new Observable(...) while operators like of, from etc would not be able to let you handle the cleanup.
Nice! Thanks for providing your example. I would have implemented this as an external Subject instead of wrapping the third-party API in a new Observable. This solution looks much cleaner. I'll adjust my comment. Thanks again!
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
I think you should clarify your thinking here because doing so can definitely be legit to wrap resources into observables and cleanup once the stream is erroring or complete. For example you could use this to create a cold observable which would add some 3D shape on a scene in webgl and whenever you unsubscribe from that observable you would run the code to remove it from the scene. Dealing with such an observable in a switchmap for example will make your life way easier and your code cleaner by encapsulating the creation and the removal of 3rd party objects/data without having to deal with both outside of this observable within some
tap
where you have to remember to do soThank you for your comments. Could you provide a specific code example? I had understood the guidance to be to use a creation function instead of
new Observable()
where possible. And that we should only usenew
when creating a Subject/BehaviorSubject. But I'm happy to correct if my information is not accurate. Thanks!Sure thing :).
Imagine you're using a library like
ngx-toastr
: npmjs.com/package/ngx-toastrwhere the API lets you create a toast message with the following API:
Now the code above will open a toast (and as soon as the default timeout is reached it'll be closed).
Let say that now, we have an HTTP call which may take super long, so we want the user to be aware of the status.
You could do the following:
What you could do instead would be something like this:
The nice about this approach IMO is that the 3rd party resource manipulation which doesn't have a reactive public API is wrapped into a cold observable (which we could subscribe to multiple times) and no one down the stream has to worry about what was created and requires to be cleaned up.
It would also let us take advantage of Rxjs further if for example we were using a retry on the HTTP call. If one HTTP call was to start: the popup would show. As soon as it's completed or throws, the popup would be closed. If we do add
retry(3)
at the end of our stream, and the call was failing the first 2 times then succeeding the third one, we'd see:In conclusion, I'd say that when you have to work with APIs that are not exposing some reactive bindings you can isolate the creation and teardown logic of those by using
new Observable(...)
while operators likeof
,from
etc would not be able to let you handle the cleanup.Hope it's clear :)!
Nice! Thanks for providing your example. I would have implemented this as an external Subject instead of wrapping the third-party API in a new Observable. This solution looks much cleaner. I'll adjust my comment. Thanks again!