Why is value undefined at .then() chained to Promise?


Given

function doStuff(n /* `n` is expected to be a positive number */) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(n * 10)
    }, Math.floor(Math.random() * 1000))
  })
  .then(function(result) {
    if (result > 100) {
      console.log(result + " is greater than 100")
    } else {
      console.log(result + " is not greater than 100");
    }
  })
}

doStuff(9)
.then(function(data) {
  console.log(data) // `undefined`,  why?
})

Why is data undefined at .then() chained to doStuff() call?

Because no Promise or other value is returned from .then() chained to Promise constructor.

Note that .then() returns a new Promise object.

The solution is to return a value or other function call which returns a value or Promise from .then().

function doStuff(n /* `n` is expected to be a positive number */) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(n * 10)
    }, Math.floor(Math.random() * 1000))
  })
  .then(function(result) {
    if (result > 100) {
      console.log(result + " is greater than 100")
    } else {
      console.log(result + " is not greater than 100");
    }
    // `return` `result` or other value here
    // to avoid `undefined` at chained `.then()`
    return result
  })
}

doStuff(9)
.then(function(data) {
  console.log("data is: " + data) // `data` is not `undefined`
});


The problem were facing:

return 
(new Promise(..)) //the promise we want to return
.then(()=>undefined) // the promise were actually returning, which resolves to undefined

As you may already noticed, then returns a new promise. This has a good reason, it makes promise chaining easy, e.g:

getUser()//an asynchronous action
 .then(user=>login(user))//then if we get the user,promise to log in
 .then(token=>console.log("logged in,token is "+token) //then if we logged in, log it
 .catch(error=>"login failed");//catch all errors from above

But this also creates the small trap, we are facing. The solution could be returning the original promise and not the new promise automatically returned by .then() as this is resolved to undefined as the function inside then is not explicitly returning something:

//what were doing:
Promise.resolve(n*10)//the original promise resolves to n*10
.then(a=>undefined)//the then gets n*10 passed as a, but returns undefined
.then(b=>console.log(b));//b will be undefined  :0 

//what we want:
var promise=Promise.resolve(n*10);
promise.then(a=>undefined);//a is n*10, this resolves to undefined
promise.then(b=>console.log(b));//but this still logs n*10, as its the original promise  :) 

So as you can see, to return the original promise, we simply store it in a variable, then assign a .then handler to it, and have still a reference to the original promise which we can assign other handlers to ( or return ).

In action:

function doStuff(n /* `n` is expected to be a number */) {

    //create a new promise and store it

    var promise=new Promise(function(resolve, reject) {
        setTimeout(function() {
           resolve(n * 10)
        },1000);
    });

    //add a then handler to this promise  

    promise.then(result=>console.log(result + " is "+result<100?"not":""+" greater than 100"));

    //return the original one

    return promise;

}

doStuff(9).then(function(data) {
  console.log(data) //not undefined, as original promise
})

The doStuff is returning the Promise. But, your last then function is not returning any values, hence data is coming as undefined.

In promises, value of arguments of the next then function is the returned value of the previous then function.

function doStuff(n /* `n` is expected to be a positive number */) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve(n * 10)
    }, Math.floor(Math.random() * 1000))
  })
  .then(function(result) {
    if (result > 100) {
      console.log(result + " is greater than 100")
    } else {
      console.log(result + " is not greater than 100");
    }
    return result;
  })
}

doStuff(9)
.then(function(data) {
  console.log(data) // `90`
})


You are not returning the result from the .then() chained to the Promise. You need to add return result; to the .then()


Because your data value is the return value of the last .then(), your last .then() doesn't have a valid return value.

So, you can add the return value in the last .then() function.


You must return the result from the .then() chained to the Promise. In your case just add

return result;

to the first .then().