Search code examples
javascripttypescripteslinttypescript-eslint

How to execute another promise in finally?


I have a promise chain and I need to write to a file using fs.writeFile after the chain is executed. As far as I can see, finally accepts a Promise as a return type:

finally(onfinally?: (() => void) | undefined | null): Promise<T>

But looking at my code, eslint doesn't seem to like it:

My Code:

    return this.downloadProject(data)
      .finally(() => {
        return fse.writeFile('', 'foo');
      })

VSCode:

enter image description here

Is this caused by a misconfiguration of my Eslint or is this a code smell?


Solution

  • The ECMAScript specification has for finally, in step 6:

    i. Let result be ? Call(onFinally, undefined).
    ii. Let promise be ? PromiseResolve(C, result).
    ...etc

    So there is a use for the value returned. If this return value happens to be a promise, then the promise returned by finally will be locked into the promise returned by its callback. However, the resolution value will still be that of the original promise.

    This can be seen in the following test script:

    Promise.resolve(3)
           .finally(() => new Promise(resolve => 
                setTimeout(() => resolve(5), 2000))
            )
           .then((value) => console.log("resolved to", value));
    
    // Message appears after 2 seconds, and value is 3

    And similarly, when the original promise is rejected, the promise returned in the finally callback is awaited, but the rejection remains:

    Promise.reject(3)
           .finally(() => new Promise(resolve => 
                setTimeout(() => resolve(5), 2000))
            )
           .catch((value) => console.log("rejected with", value));
    
    // Message appears after 2 seconds, and value is 3

    There is a clearly a consequence to returning a promise in the finally callback, so we could argue that the linter should not complain about it.

    Ignoring a rejection

    If you want to work around this linter warning, or if you really want to have the effect of a then, so that it returns a new promise with its own outcome, than weave a .catch just before a .then:

    return this.downloadProject(data)
          .catch(() => {})  // This promise will fulfill after a rejection
          .then(() => {
            return fse.writeFile('', 'foo');
          })