2

In my console.log(info), I want to get the value of "result". But when I use console.log(info), I get Promise { <pending> }:

var info=new Promise((resolve, reject) => {
  request(options, (err, res, body) => {
    if (body.match('success') || body.match('code="25"')) {
      resolve("success");
    } else {
      reject(body);
    }
  });
}).then(result => {           
  return result;
}).catch(err => {
  console.log("error: " + err)
});

console.log(info);

I would like to get info == result. How can I do it?

Thanks

Badacadabra
  • 8,043
  • 7
  • 28
  • 49
aline5188
  • 35
  • 3

2 Answers2

4

What happens here

First some explanation of what happens here. When you do something like this:

var info = new Promise((resolve, reject) => {
  // ...
}).then(result => {
  // ...
}).catch(err => {
  // ...
});

then what ends up as the value of the info variable is a promise. That's because you start with a promise returned by the new Promise() constructor, you invoke its .then() method which returns a second promise, and on that second promise you invoke the .catch() method which returns a third promise - and this third promise is what gets saved into the info variable.

All of those method calls return immediately before the original HTTP request is finished so when you reach the next line:

console.log(info);

it's impossible to access the value of the response from the request() call because it didn't happen yet (the request() callback hasn't been called yet at this point). The info variable has a promise which is an object that you can use to register the callbacks that you want run when the value is eventually available. When you pass it to the console.log() it prints that it's a promise.

How to get the value

Now, when you want to print the value when it's finally available then you can attach a promise resolution callback to the promise that you have e.g. with a code like this:

info.then((value) => {
  // you can use the value here
  console.log('Value:', value);
}).catch((err) => {
  // the promise got rejected
  console.log('Error:', err);
});

If you are using a relatively recent version of Node (7.0+) then you could use await if you are inside of a function declared with an async keyword, to get the resolution value of the promise like this:

(async function () {

  let value = await info;
  console.log(value);

})();

or shorter:

(async () => {

  console.log(await info);

})();

(If you are already inside of an async function then you don't need the (async () => { ... })(); wrapper.)

How it works

What the await keyword does is it yields the promise from an implicit generator function that passes the control to the outer coroutine control code which attaches a resolution and rejection handlers to that yielded promise and starts the generator again by either returning the resolved value from the await operator or raising an exception if the promise was rejected. The generator can then continue using the return value and catching the exception, or it can let the exception bubble to the outer blocks where it can be caught or converted to a rejection of the implicit promise returned by the async function if not handled along the way.

How you can use it

It may seem complicated but from the point of view of your code it lets you do things like:

let value = await fun();

(where fun() is a function that returns a promise) and have the resolved value of the promise available in the value variable (the real value, not a promise).

Remember that the await keyword throws an exception when the promise in question gets rejected. To handle that case you can use a try/catch block:

try {
  let value = await fun();
  // promise got resolved, you have value here:
  console.log('Value:', value);
} catch (error) {
  // promise got rejected, you have error here:
  console.log('Error:', error);
}

which is equivalent of this code without the new syntax:

fun().then((value) => {
  // promise got resolved, you have value here:
  console.log('Value:', value);
}).catch((error) => {
  // promise got rejected, you have error here:
  console.log('Error:', error);
});

with the main difference being the variable scoping when you await on multiple promises in a single try block as opposed to using multiple chained .then() handlers, each returning a new promise.

Your code looked like it used await but it didn't

I added the example of how to fix your code with await because you wrote your code as if this:

var info = new Promise(...);
// here the 'info' variable is set to a promise

was really this:

var info = await new Promise(...);
// here the 'info' variable is set to the value
// that the promise eventually resolves to

More info

For more info, see:

Node support

For support of that syntax in Node versions, see:

In places where you don't have native support for async and await you can use Babel:

or with a slightly different syntax a generator based approach like in co or Bluebird coroutines:

rsp
  • 107,747
  • 29
  • 201
  • 177
  • 1
    This does not teach or explain to the OP how to understand what is wrong with their original code and doesn't even really explain why this would work either. Good answers will explain what was wrong, explain what needs to be done differently and why and show an example. With your reputation, I certainly would think you would know that. And, for the OP's info, there is no need to use `await` to solve their problem. It is one choice, but certainly not required. A simple `.then()` will work too. – jfriend00 May 02 '17 at 21:42
  • battle of the titans – Alexander Mills May 02 '17 at 22:10
  • @jfriend00 Those are very good points. I improved my answer by adding more detailed explanations. Thanks for you suggestions. – rsp May 02 '17 at 23:18
4

In your code, info is a promise. Thus, you don't compare info to anything. To get access to the result in a promise, you use .then() on the promise as in:

info.then(result => {
    // test result here
}).catch(err => {
    // handle error here
});

In addition, your .catch() handler is "eating" the error after logging it. If you don't rethrow the error or return a rejected promise from a .catch() handler, then the error is considered "handled" and the promise state changes to fulfilled.

So, you can do this:

let info = new Promise((resolve, reject) => {

    request(options, (err, res, body) => {
        if (body.match('success') || body.match('code="25"')) {
            resolve("success");
        } else {
            reject(body);
        }
    });
}).catch(err => {
    console.log("error: " + err);
    // rethrow error so promise stays rejected
    throw err;
});

info.then(result => {
    // test result here
}).catch(err => {
    // handle error here
});
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • thanks very much,but if I use info.then(result => { // test result here }).catch(err => { // handle error here }); how can I get my value result? I realy want in the end a value like "let anwser=result", I don't want just have the the value result in the info.then(result=>... – aline5188 May 02 '17 at 22:20
  • @aline5188 - Since it looks like you may be new here, if this answered your question, you can indicate that to the community by clicking the green checkmark to the left of the answer and that will also earn you some reputation points here. – jfriend00 May 02 '17 at 22:26
  • 1
    @aline5188 - You can't do that with asynchronous operations. The value is not available until sometime later and the only place you can use it is inside the `.then()` handler. That's how async development works in Javascript and it's one of the things you need to learn when programming with asynchronous operations. The rest of your code that wants to use that value just goes inside of the `.then()` handler or in a function that you call from there. This will seem odd to you at first, but you will get used to it fairly quickly. – jfriend00 May 02 '17 at 22:28