skip to Main Content

Per this fiddle, if I try to call a second promise function from the .finally() block of the first promise, the second promise resolves but the resolve value is "undefined".
However, if I call the second promise function in a .then block after the .finally block, everything works as expected. Why can’t I call the second promise in the .finally block of the first promise?

Example that fails (Promise 2 value is "undefined"): https://jsfiddle.net/scemohkb/

function res(v) { return new Promise((resolve, reject) => { resolve(v); }) }
function rej(v) { return new Promise((resolve, reject) => { reject(v); }) }

rej(1)
.then((val) => {
    console.log(`Value1: ${val}`);
})
.catch((e) => {
    console.error(`Caught 1: ${e}`);
})
.finally(() => {
    console.log("Finally 1");
    return res(2);
})
.then((v) => {
    console.log(`Value2: ${v}`); // THIS DOESN'T WORK: v is undefined
})
.catch((e) => {
    console.log(`Caught 2: ${e}`);
})
.finally(() => {
    console.log("Finally 2");
});

Example that works: (Promise 2 returns the correct value): https://jsfiddle.net/scemohkb/1/

function res(v) { return new Promise((resolve, reject) => { resolve(v); }) }
function rej(v) { return new Promise((resolve, reject) => { reject(v); }) }

rej(1)
.then((val) => {
    console.log(`Value1: ${val}`);
})
.catch((e) => {
    console.error(`Caught 1: ${e}`);
})
.finally(() => {
    console.log("Finally 1");
})
.then(() => {
    return res(2);
})
.then((v) => {
    console.log(`Value2: ${v}`); // This works: v is the correct value.
})
.catch((e) => {
    console.log(`Caught 2: ${e}`);
})
.finally(() => {
    console.log("Finally 2");
});

In example 1, the second promise function still returns correctly upon calling resolve() and the .then block is executed, but the value sent to resolve() is ignored and instead the .then() function receives "undefined".

What is different about the .finally() function that I’m missing, that prevents this from happening? It would seem to make sense for the cleanest chaining of multiple promises that can resolve or reject independently, vs. adding another .then() function after the .finally().

2

Answers


  1. Directly copy pasting from the docs:

    A finally() call is usually transparent and does not change the eventual state of the original promise. So for example:

    Unlike Promise.resolve(2).then(() => 77, () => {}), which returns a promise eventually fulfilled with the value 77, Promise.resolve(2).finally(() => 77) returns a promise eventually fulfilled with the value 2.
    Similarly, unlike Promise.reject(3).then(() => {}, () => 88), which returns a promise eventually fulfilled with the value 88, Promise.reject(3).finally(() => 88) returns a promise eventually rejected with the reason 3.

    The only exception to the above rule is when finally() itself rejects (by throwing a value).


    .finally() resolves with the value that the .then() and .catch() resolve with. The main reason this does not work is there is no return from inside the handlers of from then() and catch() and hence no resolution/rejection. That is why finally() also does not resolve/reject with any value.

    If you return a value from inside the handlers of then() and catch(), then the handler of finally() can resolve/reject with that value.

    function res(v) { return new Promise((resolve, reject) => { resolve(v); }) }
    function rej(v) { return new Promise((resolve, reject) => { reject(v); }) }
    
    rej(1)
    .then((val) => {
        console.log(`Value1: ${val}`);
    })
    .catch((e) => {
        console.error(`Caught 1: ${e}`);
        return e;
    })
    .finally(() => {
        console.log("Finally 1");
        //return res(2); //This is useless, and no matter what is put here will have no effect
        throw (5); //This is the only exception that can work
    })
    .then((v) => {
        console.log(`Value2: ${v}`);
    })
    .catch((e) => {
        console.log(`Caught 2: ${e}`);
    })
    .finally(() => {
        console.log("Finally 2");
    });

    Uncomment the above code to play around with it:

        //return res(2); //This will have no effect
        throw (5); //This is the only exception that can work
    

    Reading: This is what the docs mention about return values from inside the handler function of then():

    The behavior of the returned promise (call it p) depends on the handler’s execution result, following a specific set of rules. If the handler function returns a value: p gets fulfilled with the returned value as its value.

    Login or Signup to reply.
  2. The finally() function return value is ignored unless it is a rejected promise:

    A function to asynchronously execute when this promise becomes settled. Its return value is ignored unless the returned value is a rejected promise. The function is called with no arguments.

    If you want to handle the res(2) Promise with those functions, you have to do so within finally:

    function res(v) { return new Promise((resolve, reject) => { resolve(v); }) }
    function rej(v) { return new Promise((resolve, reject) => { reject(v); }) }
    
    rej(1)
    .then((val) => {
        console.log(`Value1: ${val}`);
    })
    .catch((e) => {
        console.error(`Caught 1: ${e}`);
    })
    .finally(() => {
        console.log("Finally 1");
      
      res(2)
        .then((v) => {
                console.log(`Value2: ${v}`);
          })
          .catch((e) => {
            console.log(`Caught 2: ${e}`);
          })
          .finally(() => {
            console.log("Finally 2");
          })
    });
    

    That will result in the output:

    "Finally 1"
    "Value2: 2"
    "Finally 2"
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search