I can't get the value of “result” in Node.js

2019-05-19 23:12发布

问题:

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

回答1:

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:

  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

Node support

For support of that syntax in Node versions, see:

  • http://node.green/#ES2017-features-async-functions

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

  • https://babeljs.io/docs/plugins/transform-async-to-generator/

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

  • https://www.npmjs.com/package/co
  • http://bluebirdjs.com/docs/api/promise.coroutine.html


回答2:

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
});