JavaScript Promises in Depth

ES2015 brought a native Promise implementation to the JavaScript standard library. In this course, we’re going to take an in-depth look at how to use promises to model asynchronous operations in JavaScript.

Watch the Course

Using Promise.prototype.finally() in TypeScript

ES2018 introduced a new Promise.prototype.finally() method to the standard library. The finally() method lets you execute a callback function once the promise that it's called on has been settled (that is, it has been either fulfilled or rejected and is therefore no longer pending):

somePromise.finally(() => {
  // Code
});

The main benefit of using finally() over then() with two callbacks is that you don't have to duplicate code in both the fulfillment and the rejection handler:

somePromise.then(
  result => {
    // Code
    return result;
  },
  error => {
    // Code
    throw error;
  }
);

Check out the ES2018: Promise.prototype.finally() post by Axel Rauschmayer for more practical use cases.

Using the finally() Method in TypeScript

The TypeScript compiler doesn't have to do anything special to compile calls to the finally() method. However, it might issue a type error like this:

// error TS2339: Property 'finally' does not exist
// on type 'Promise<Response>'.
somePromise.finally(() => {
  // ...
});

If you see this error, the compiler is telling you that it doesn't know anything about a finally() method on the Promise type. In that case, the JavaScript version you're targeting (typically ES5 or ES2015) doesn't define a finally() method on the Promise prototype.

The solution to make the type error go away is to assure the TypeScript compiler that at run-time, the finally() method is going to be available (either natively or via a polyfill). Head over to your project's tsconfig.json file and add the value "es2018.promise" to the "lib" array:

{
  "compilerOptions": {
    // ...
    "lib": ["dom", "es2015", "es2018.promise"]
  }
}

This will include the es2018.promise.d.ts type declaration file in the compilation which defines the finally() method on the Promise interface:

/**
 * Represents the completion of an asynchronous operation
 */
interface Promise<T> {
    /**
     * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The
     * resolved value cannot be modified from the callback.
     * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).
     * @returns A Promise for the completion of the callback.
     */
    finally(onfinally?: (() => void) | undefined | null): Promise<T>
}

With this type declaration in place, the TypeScript compiler should no longer report a type error when you use the finally() method:

somePromise.finally(() => {
  // Code
});

It's now your responsibility to make sure that the finally() method is actually available in all of your target browsers, either natively or via a polyfill.

Browser Support

The browser support for the finally() method is quite good (see caniuse.com) — as of November 2018, all major browsers implement it natively:

Browser support for the Promise.prototype.finally() method from caniuse.com

If you need to support a browser that doesn't implement the finally() method natively, make sure to include a polyfill in your application (e.g. via the promise.prototype.finally npm package). Note that if Promise itself isn't supported in any of your target browsers, you'll need to polyfill that separately.