0

I have Nodejs code using request library to get the authentication token from a url.

var request = require("request")

const wellish_dev_url = "https://dev.wellish.com"

function get_auth_token(){
    const  api_end_point = wellish_dev_url + "/api/v1/auth"

    var options = {
        url: api_end_point,
        headers: {"Content-Type": "application/json"},
        auth: {
            'user': 'admin',
            'pass': 'password'}
    }

    var r = request.get(options, function(error, response, body){
        if(!error && response.statusCode==200){
            var token = JSON.parse(body)
            var auth_token = token["data"][0]["accessToken"]
            // console.log(auth_token)
            return auth_token
        }
        else{
            console.log("Code : " + response.statusCode)
            console.log("error : " + error)
            console.log('body : ' + body)
        }
    })
}

// get_auth_token()
var auth_token_new = get_auth_token()
console.log(auth_token_new)  

I want to return the auth_token to be used as an argument in another function. However, it shows undefined.

I look up online to use cb, however, I have no idea how it works. I really want to return the value without using cb. Any helps?

jo_va
  • 13,504
  • 3
  • 23
  • 47
Akira
  • 273
  • 5
  • 15
  • `cb` simply means `callback` - and it works like any other callback would – Derek Pollard Apr 30 '19 at 21:11
  • Possible duplicate of [Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference](https://stackoverflow.com/questions/23667086/why-is-my-variable-unaltered-after-i-modify-it-inside-of-a-function-asynchron) – zero298 Apr 30 '19 at 21:12
  • Can I do it without using cb? – Akira Apr 30 '19 at 21:12
  • I suggest to return a Promise from your function, and resolve it in the inner callback, I can provide an answer with an example if that can help – jo_va Apr 30 '19 at 21:16
  • @jo_va Or you could refer to the proposed duplicate since it pretty much covers every possible topic with respect to asynchronous code in JavaScript. – zero298 Apr 30 '19 at 21:17
  • @zero298, you are totally right indeed. – jo_va Apr 30 '19 at 21:18
  • You might find [this](https://stackoverflow.com/q/247483/9180019) question/answer useful, since the solution does not include promises _per se_ - good old _XHR_, however in terms of a long run I strongly suggest to get familiar with _promises_, then _async/await_ - (as a 'bonus' feature they simplify life a lot ;) ) – the0ffh Apr 30 '19 at 21:48

1 Answers1

2

Your callback is only executed once the response comes back from the server. By that time, your function will already have been executed.

Since your code is asynchronous, you need a means to return the response only when you get it back from the server.

You can use a Promise for that task.

Here is an example on how to solve it in your particular case. Use the Promise constructor to return a Promise which resolves/rejects when your inner callback gets called.

Then you simply have to chain a .then() to your function call to get the token:

...

function get_auth_token() {
    const  api_end_point = ...
    const options = ...

    return new Promise((resolve, reject) => {
        var r = request.get(options, (error, response, body) => {
            if (!error && response.statusCode == 200){
                var token = JSON.parse(body)
                var auth_token = token["data"][0]["accessToken"]
                resolve(auth_token)
            } else {
                console.log("Code : " + response.statusCode)
                console.log("error : " + error)
                console.log('body : ' + body)
                reject(error)
            }
        })
    });
}

get_auth_token()
    .then(token => console.log(token))
    .catch(error => console.error(error));

You can also use await to get the value from the Promise like this, just make sure you are using await in a function marked async:

async function parent_function() {
    function get_auth_token() { ... }

    try {
        const token = await get_auth_token();
        console.log(token);
    } catch (error) {
        console.log(error);
    }
}
jo_va
  • 13,504
  • 3
  • 23
  • 47
  • I am very new to Nodejs, and I have no idea we have to use Promise and callback, I just want to return the value simply like how python does. Is there any way to return without using Promise and callback – Akira Apr 30 '19 at 21:28
  • @Akira, your callback is executed later in time, once a response comes back from the server. So the code is inherently asynchronous. Your function will be done executing before the response from the server will come, so you need a means to execute your code only once this response comes back, you can use a Promise for that. – jo_va Apr 30 '19 at 21:31
  • Thanks for the answer, I have used Python before, so why don't we consider the async using python? Every time I want to return some value using Nodejs, I need to consider the async/sync, right? – Akira Apr 30 '19 at 21:35
  • @Akira, yes, but most libraries in JavaScript will already return you a Promise, if they don't you can use the above trick to return a Promise from which you can access the value when it becomes available – jo_va Apr 30 '19 at 21:38
  • I think your solution works, one more question, how can I assign a new variable to the value returned from the function? I have tried the `var auth_token_new = get_auth_token().then(token => console.log(token))` However, it also print out additional stuff which I don't want `Promise{}` – Akira Apr 30 '19 at 21:52
  • 1
    While using both `resolve` and `reject` it would be nice to provide a line responsible for handling the second one as well, e.g: `get_auth_token().then(console.log).catch(console.log)` or `get_auth_token().then(result => { console.log(result) }).catch(error => { console.log(error) })` they both result's are the same in this case. To avoid confusion stick to the second example (first is for OP's further research ;)). – the0ffh Apr 30 '19 at 21:53
  • @Akira, if you want to get the value with an assignment, I suggest to use async/await, otherwise, you will have to access the value from within `.then(...)`, what you are getting is the raw value of the Promise and not the "resolved" value – jo_va Apr 30 '19 at 21:59
  • @the0ffh Thanks for the input, I am just wondering whether or not I can assign a new variable for that – Akira Apr 30 '19 at 22:00
  • @jo_va could you provide an example using async/ await? I still don't understand why we cannot assign a new variable using Promise, since my goal is to return the value first, then assign it to a variable, then use that variable as argument in the other functions – Akira Apr 30 '19 at 22:03
  • 1
    @Akira, take a look at [this question](https://stackoverflow.com/questions/46355664/get-the-value-of-a-promise-and-assign-to-variable/46355826) - the answer describes how can you assign results of `promise` / `async/await` using example provided by @jo_va – the0ffh Apr 30 '19 at 22:26
  • 2
    @Akira, I suggest you read some material on Promise. This was already explained, using `.then` and `async/await`, see my updated answer. – jo_va Apr 30 '19 at 22:57
  • let say you have a following promise: `let test = (param) => Promise ((resolve, reject)=> ...bla,bla...)` to operate on it's return values, you have to do the following: `test.then(returnValue => {let myVariable = returnValue; console.log(myVariable)}).catch(error => {console.log(error)})` + as @jo_va suggested, read _MDN docs_ regarding _promises_ ie. google -> _mdn promises_ – the0ffh Apr 30 '19 at 23:00