What Does The Finalize Operator Do In Angular/RxJS

I’ve recently gotten into the habit of using the finalize operator when using RxJS in Angular. For the most part, if you’ve used languages that have try/catch statements, they generally have a “finally” statement too. For example, in plain old javascript you can do something like :

try {
    DoWork();
} catch {
   //An exception was thrown here
}
finally {
   //Regardless if an exception was thrown or not, always run this block of code next. 
   CleanUp();
}

The finalize operator in RxJS really isn’t that different.

Let’s imagine that we have a piece of code that calls an API endpoint. When calling this API endpoint, I want to ensure that I lock the UI so that while we are saving any data, the user doesn’t click anything. To lock the UI, I have a simple boolean variable. The code might look something like so :

lockUi : boolean;
DoWork() {
    this.lockUi = true;
    this.CallAPI()
    .subscribe(result => {
        //Do something with the result.
        //Unlock the UI because we are done. 
        this.lockUi = false; 
    });
}
CallAPI() {
    //Call an API here and return an observable (But for now, mock it)
    return of();
}

Works well but what if our CallAPI method errors? What happens then? Our UI would be locked forever, which we may not want. So instead what you might do is try and change the DoWork method to capture the error and handle that use case too. We can do this within our subscribe callback like so :

DoWork() {
    this.lockUi = true;
    this.CallAPI()
    .subscribe(result => {
        //Do something with the result.
        //Unlock the UI because we are done. 
        this.lockUi = false; 
    }, 
    error => {
        this.lockUi = false;
    });
}

This may seem OK since we are only duplicate one call to unlock the UI, but what if we keep adding additional things that we want to say “No matter what, always do this on error or on success”. Seems a little silly to copy and paste everytime. And that’s why finalize is so handy!

We can take the above and change it to :

DoWork() {
    this.lockUi = true;
    this.CallAPI()
    .pipe(
        //Finalize to always unlock UI no matter what. 
        finalize(() => this.lockUi = false) 
    )
    .subscribe(result => {
        //Do something with the result. 
    });
}

And that’s all there is to it! A nice handy way to ensure that code gets run no matter the observable outcome!

Leave a Reply

Your email address will not be published.