3
function  f () {
   return new Promise(function (resolve, reject) {

        resolve(4);
    })
}

function  g () {
    return f().then((res) => {return res;})

}

console.log(g());

This returns Promise { <pending> }

If I returned res(in the then) and then returned f(), why isn't the output 4?

user7361276
  • 229
  • 2
  • 4
  • 11

2 Answers2

6

a valid answer would be:

function f() {
    return new Promise(function(resolve, reject) {

        resolve(4);
    })
}

function g() {
    return f().then((res) => {
        return res;
    })
    .then((res) =>{
        console.log(res);
    })

}
g()

Why? Any time you return from inside a then statement in a promise, it passes it to the next statement (then or catch). Try commenting out return res and you'll see that it prints undefined.

==============
However, with ES7 we can use async/await. We can replicate the above using the following code:

function f() {
  return new Promise(function(resolve, reject) {
    resolve(4);
  });
}

async function g() {
  var a = await f();
  // do something with a ...
  console.log(a);
}

g();

It's important to note that console.log(g()) still returns a promise. This is because in the actual function g, resolving the promise gets delayed and therefore doesn't block the rest of our code from executing but the function body can utilize the returned value from f.

NOTE: to run this you need node 7 and it should be executed with the --harmony-async-await option.

===========
EDIT to include new code snippet
Look at the following code. You must use then to access the previous objects - however, where you access it in this case is up to you. You can call then on each promise inside of Promise.all, in this case .then((userVictories) => ...).then(...) or once Promise.all returns. It's important to note that Promise.all returns once all promises it contains resolve.

var membersArray = groupFound.members;
Promise.all(membersArray.map((member) => {
  return db.doneTodo.find({ 'victor._id': member._id }).then((userVictories) => {
    return {
      email: member.email,
      victories: userVictories.length,
    }
  }).then(obj => {
    /*
        obj is each object with the signature:
            {email: '', victories: ''}

            calling this then is optional if you want to process each object
            returned from '.then((userVictories) =>)'

            NOTE: this statement is processed then *this* promise resolves

            We can send an email to each user with an update
     */
  });
}))
  .then((arr) => {
    /*
        arr is an array of all previous promises in this case:
        [{email: '', victories: ''}, {email: '', victories: ''}, ...]

        NOTE: this statement is processed when all of the promises above resolve.

        We can use the array to get the sum of all victories or the 
        user with the most victories
     */
  })
andy9775
  • 372
  • 3
  • 10
  • But here: `var membersArray = groupFound.members; Promise.all(membersArray.map((member) => { return db .doneTodo .find({'victor._id': member._id}) .then((userVictories) => { return { email: member.email, victories: userVictories.length } }); }))` We didn't need to attach a "then" in front of the object? – user7361276 Dec 31 '16 at 20:05
  • 'We didn't need to attach a "then" in front of the object?', Which object? `db.doneTodo.find` returns a promise after which you call `.then((userVictories) => ...`. I don't fully understand your comment – andy9775 Dec 31 '16 at 20:11
  • The last object with "email" and "victories..." – user7361276 Dec 31 '16 at 20:12
  • `membersArray` I'm assuming is an array of objects which have an email and id property (`{email: 'test@test.com', id:1}`). By calling map, you iterate over each member and call `db.doneTodo.find` which resolves a promise. Inside of your then block `.then((userVictories) => ...)` you handle its result. Promise.all will return once all promises in the array resolve and thus it ends up being an array of objects `{email: ..., victories: ...}`. In order to access these objects you need to call then like so `Promise.all(...).then(arr => console.log(res);)` – andy9775 Dec 31 '16 at 20:25
  • Do `var a = Promise.all(...)` and `console.log(a)` it should print a promise – andy9775 Dec 31 '16 at 20:29
  • Yes, I see that, but my point was, since we are returning `{ return { email: member.email, victories: userVictories.length }` inside a `then`, dont we need another then after this FOR this object? – user7361276 Dec 31 '16 at 20:31
  • I see, no unless you want to process `{ email: member.email, victories: userVictories.length }` further. But again, if you want to access the results you need to use `Promise.all(...).then(/* [{email: 'test', victories:1}]'*/)` – andy9775 Dec 31 '16 at 20:32
  • I thought you said that we needed another "then" if we use return inside a previous "then?" – user7361276 Dec 31 '16 at 20:46
  • No. When calling `return` inside of a then statement, the value returned from the first `then` is available inside the next `then` statement. Therefore in order to access the values from the previous `then`, you need to process them inside of the next `then` - they are not accessible otherwise. You cannot return from a then method and have the value returned available outside of it (you **initial** question). Hence my answer above 'I see, no unless you want to process { email: member.email, victories: userVictories.length } further. '. The second code snippet is different from the initial one. – andy9775 Dec 31 '16 at 21:41
  • Look at my second answer `Promise.all(/* your code */).then(...)` This is where you would normally call the then statement to access the results of Promise.all. Yes - you do need to use `then` to access the results of a promise (unless you use async/await), but in your second example it should be after `Promise.all`. – andy9775 Dec 31 '16 at 21:43
  • But how does using return {email: ... id: .....} replace the old item inside the array? We used return, but I dont get how it replaces the item with map – user7361276 Dec 31 '16 at 22:04
  • The returned array after calling map is an array of promises. Calling `return db.doneTodo .find(...)` returns a promise for each entry in the `membersArray`. Each Promise in the array resolves to `{email: ... id: .....}`. Look at my edited answer – andy9775 Dec 31 '16 at 22:18
  • I see that it resolves to `return {email: ... id: .....}`, but then what happens? Can you use return... to replace the item using map? Wouldn't you get promise pending or undefined? – user7361276 Dec 31 '16 at 22:24
  • do you mean have the function passed to map return `{email: ... id: .....}` rather than a promise? No, but Promise.all does that for you. It ensures that instead of an array of promises, you get an array of results from each promise in the array passed to Promise.all – andy9775 Dec 31 '16 at 22:26
  • Yes, we passed `{email: ... id: .....}` to map rather than a promise? So Map does that for us? – user7361276 Dec 31 '16 at 22:31
  • Also, what was the use of return db...? – user7361276 Dec 31 '16 at 22:32
  • We can't simply return `{email:..., id:...}` and have it accessible from outside the promise - we need to resolve it in order to get the result. `db.doneTodo.find` is a promise so `return db.doneTodo.find` returns a promise. `membersArray.map(...)` results in `[Promise, Promise, Promise, ...]`. Promise.all resolves each promise in an array its passed (e.g. `Promise.all([Promise, Promise, Promise])` and makes their results available to a `then` statement called **after** `Promise.all` – andy9775 Dec 31 '16 at 22:35
  • What would happen without the "return?" – user7361276 Dec 31 '16 at 22:36
  • we would get an array of undefined . Its like `console.log([1,2,3].map(val => {val + 1}))` vs `console.log([1,2,3].map(val => { return val + 1}))` one is `[ 2, 3, 4 ]` the other is `[ undefined, undefined, undefined ]` – andy9775 Dec 31 '16 at 22:40
  • Try playing with this code `var membersArray = [ { _id: 1, email: '1', }, { _id: 2, email: 2, }, ]; Promise.all(membersArray.map((member) => { return Promise.resolve([ ...Array(Math.round(Math.random() * 20)).keys(), ]) .then((userVictories) => { return { email: member.email, victories: userVictories.length, } }); })).then(res => console.log(res));` as its very similar to what your asking about – andy9775 Dec 31 '16 at 22:43
0

Let's look at jQuery first as an introduction to learning promises.

What does this code return? What is the value of result?

var result = $('body');

Hint: It won't be the <body/> body HTML element.

result is a jQuery collection object. Internally it holds a reference to the body tag. But the actual result object is a collection.

What does this return?

var result = $('body').css('background', 'red');

Again, it returns a jQuery collection.

And this?

var result = $('body').css('background', 'red').animate({height: "20px"});

Same thing. A jQuery collection.

Now, what does this promise based code return?

var result = new Promise();

It's probably clear this returns a promise. But what about this code?

var result = new Promise().resolve().then(() => {
    return 'Hello';
});

What is the value of result now? Hint: It's not the string 'Hello'.

It's a promise!

What does this return?

var result = new Promise().resolve().then(() => {
    return new Promise().resolve();
}).then(() => {
    return 'Hello';
}).catch(() => {
    console.log('Something went wrong');
});

It returns a promise! Promises let us access values in functions that are called at later times. Until the function is executed, you won't have access to whatever the promise "returns," or "resolves" with. Once you enter a promise chain, you always have to use .then(fn) to handle the next step in the program flow.

Javascript is asynchronous. All top level code is executed in order without pausing. The promise resolves at a later time, long after your console.log has finished executing. To get values back, you need to stay in promise chain land:

g().then( result => console.log(result) );
Andy Ray
  • 30,372
  • 14
  • 101
  • 138
  • Thanks! What happens here though: `Promise.all(membersArray.map((member) => { return db ///NEED THE RETURN HERE .doneTodo .find({'victor._id': member._id}) .then((userVictories) => { return { email: member.email, victories: userVictories.length } }); })).then( (res) => console.log(res)) ` ? – user7361276 Dec 31 '16 at 20:26
  • There is nothing after `{ return { email: member.email, victories: userVictories.length }` – user7361276 Dec 31 '16 at 20:26
  • The chained `.then()` after a Promise.all will be an array holding the values of each resolved promise https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all – Andy Ray Dec 31 '16 at 20:37
  • Your code looks fine on on the surface. Try adding a catch at the end to see if an error is getting eaten https://gist.github.com/AndrewRayCode/6495e7c0dc0f2370184c90ead7a528ea – Andy Ray Dec 31 '16 at 20:38
  • How does one resolve a promise? Does `{ return { email: member.email, victories: userVictories.length }` resolve the promise? – user7361276 Dec 31 '16 at 20:48
  • yes returning inside a promise will make the next `.then()` get the value of that return – Andy Ray Dec 31 '16 at 21:14
  • But we dont have a then(), and the next then, takes the whole array (the res) – user7361276 Dec 31 '16 at 21:25