-3

I recently restructured (flattened) this chain. However I am confused as to why this chain still works in any case even though the return statement in one of the .then links is conditional:

function addList(name) {
let listObj = {};
listObj.name = name;
return nameExists(name) //returns a promise<boolean>
    .then((result) => {
        console.log("foo");
        return result; //without this return statement the chain would break, 
                       //which makes sense to me because it does not return a 
                       //promise otherwise.
    })
    .then(bool => {
        listObj.unique = !bool;
        if (validListID(name)) { //this is a synchronous regex function
            listObj.unique = false;
        }
        if (!listObj.unique)
            return Counters.getNewShortListId(); // returns Promise
        //Does not return promise when condition not met.
        //However the next chain gets called in any case.
    })
    .then((id) => { //this gets called even when listObj.unique = true,
                    //also this works perfectly, why?
        listObj.__id = id;
        return new List(listObj).save();
    });
}

I am really confused as to why this behaves the way it does. I thought the promise chain breaks when no promise is returned?

tlt
  • 358
  • 4
  • 10
  • [`Promise.prototype.then`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) -> [*Return value*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#Return_value): _"... - doesn't return anything, the promise returned by `then` gets resolved with an `undefined` value; ..."_ – Andreas Aug 10 '19 at 11:17
  • 1
    JavaScript functions *in general*: if you don't explicitly return a value, they return undefined. – jonrsharpe Aug 10 '19 at 11:18
  • Possible duplicate of [How to return from a Promise's catch/then block](https://stackoverflow.com/questions/32032588/how-to-return-from-a-promises-catch-then-block) – Julian Aug 10 '19 at 11:25

1 Answers1

2

If you don't return a Promise from a .then, the next .then will continue to work just fine (since no error was thrown), but the parameter for its callback will always be undefined:

Promise.resolve()
  .then(() => {
    // don't return anything
  })
  .then((param) => {
    console.log(param);
  });

If you're sure that getNewShortListId, if, when it resolves, always resolves to a value that is not undefined, simply check that the next .then's id is not undefined.

.then((id) => {
    if (id !== undefined) {
        listObj.__id = id;
        return new List(listObj).save();
    }
});

Another option is to have the upper .then create the Promise instead of having the lower .then:

if (!listObj.unique)
  return Counters.getNewShortListId()
    .then((id) => {
      listObj.__id = id;
      return new List(listObj).save();
    })

If you don't like the look of the nested .thens, you could convert the nested .then callback to a named function that you declare beforehand (eg const processId = (id) => ...).

You can also throw an error (eg if (listObj.unique) throw new Error()) to completely break out of the current .then chain, and control flow will immediately go to the next .catch, skipping intermediate .thens - but errors generally shouldn't be used for control flow.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320