Async Function Not Returning Value, But Console.Log() Does: How to Do

Async function not returning value, but console.log() does: how to do?

As for your comment; I'll add it as answer.

The code you write in JavaScript is run on one thread, that means that if your code could actually wait for something it will block any of your other code from getting executed. The event loop of JavaScript is explained very well in this video and if you like to read in this page.

A good example of blocking code in the browser is alert("cannot do anything until you click ok");. Alert blocks everything, the user can't even scroll or click on anything in the page and your code also blocks from executing.

Promise.resolve(22)
.then(x=>alert("blocking")||"Hello World")
.then(
x=>console.log(
"does not resolve untill you click ok on the alert:",
x
)
);

Run that in a console and you see what I mean by blocking.

This creates a problem when you want to do something that takes time. In other frameworks you'd use a thread or processes but there is no such thing in JavaScript (technically there is with web worker and fork in node but that's another story and usually far more complicated than using async api's).

So when you want to make a http request you can use fetch but fetch takes some time to finish and your function should not block (has to return something as fast as possible). This is why fetch returns a promise.

Note that fetch is implemented by browser/node and does run in another thread, only code you write runs in one thread so starting a lot of promises that only run code you write will not speed up anything but calling native async api's in parallel will.

Before promises async code used callbacks or would return an observable object (like XmlHttpRequest) but let's cover promises since you can convert the more traditional code to a promise anyway.

A promise is an object that has a then function (and a bunch of stuff that is sugar for then but does the same), this function takes 2 parameters.

  1. Resolve handler: A function that will be called by the promise when the promise resolves (has no errors and is finished). The function will be passed one argument with the resolve value (for http requests this usually is the response).
  2. Reject handler: A function that will be called by the promise when the promise rejects (has an error). This function will be passed one argument, this is usually the error or reason for rejection (can be a string, number or anything).

Converting callback to promise.

The traditional api's (especially nodejs api's) use callbacks:

traditionalApi(
arg
,function callback(err,value){
err ? handleFail(err) : processValue(value);
}
);

This makes it difficult for the programmer to catch errors or handle the return value in a linear way (from top to bottom). It gets even more impossible to try and do things parallel or throttled parallel with error handling (impossible to read).

You can convert traditional api's to promises with new Promise

const apiAsPromise = arg =>
new Promise(
(resolve,reject)=>
traditionalApi(
arg,
(err,val) => (err) ? reject(err) : resolve(val)
)
)

async await

This is what's called syntax sugar for promises. It makes promise consuming functions look more traditional and easier to read. That is if you like to write traditional code, I would argue that composing small functions is much easier to read. For example, can you guess what this does?:

const handleSearch = search =>
compose([
showLoading,
makeSearchRequest,
processRespose,
hideLoading
])(search)
.then(
undefined,//don't care about the resolve
compose([
showError,
hideLoading
])
);

Anayway; enough ranting. The important part is to understand that async await doesn't actually start another thread, async functions always return a promise and await doesn't actually block or wait. It's syntax sugar for someFn().then(result=>...,error=>...) and looks like:

async someMethod = () =>
//syntax sugar for:
//return someFn().then(result=>...,error=>...)
try{
const result = await someFn();
...
}catch(error){
...
}
}

The examples allways show try catch but you don't need to do that, for example:

var alwaysReject = async () => { throw "Always returns rejected promise"; };
alwaysReject()
.then(
x=>console.log("never happens, doesn't resolve")
,err=>console.warn("got rejected:",err)
);

Any error thrown or await returning a rejected promise will cause the async function to return a rejected promise (unless you try and catch it). Many times it is desirable to just let it fail and have the caller handle errors.

Catching errors could be needed when you want the promise to succeed with a special value for rejected promises so you can handle it later but the promise does not technically reject so will always resolve.

An example is Promise.all, this takes an array of promises and returns a new promise that resolves to an array of resolved values or reject when any one of them rejects. You may just want to get the results of all promises back and filter out the rejected ones:

const Fail = function(details){this.details=details;},
isFail = item => (item && item.constructor)===Fail;
Promise.all(
urls.map(//map array of urls to array of promises that don't reject
url =>
fetch(url)
.then(
undefined,//do not handle resolve yet
//when you handle the reject this ".then" will return
// a promise that RESOLVES to the value returned below (new Fail([url,err]))
err=>new Fail([url,err])
)
)
)
.then(
responses => {
console.log("failed requests:");
console.log(
responses.filter(//only Fail type
isFail
)
);
console.log("resolved requests:");
console.log(
responses.filter(//anything not Fail type
response=>!isFail(response)
)
);
}
);

Async Await not returning value as expected

You're not quite using it properly. Every async function returns a Promise but if you don't have an explicit return statement, it will return a resolved promise with a value of undefined. That is why you see Token1 as undefined. Your GetAuthTokenAsync runs the call, but it doesn't await the result and doesn't return anything.

I have marked up your code to show what changes would be needed below.

async function GetAuthTokenAsync() {
// Initialize the 2-legged OAuth2 client, set specific scopes and optionally set the `autoRefresh` parameter to true
// if you want the token to auto refresh
var autoRefresh = true; // or false

var oAuth2TwoLegged = new ForgeSDK.AuthClientTwoLegged(
FORGE_CLIENT_ID,
FORGE_CLIENT_SECRET,
["data:read", "data:write"],
autoRefresh
);

return oAuth2TwoLegged.authenticate().then( // <---- Must return a promise
function (credentials) {
// The `credentials` object contains an access_token that is being used to call the endpoints.
// In addition, this object is applied globally on the oAuth2TwoLegged client that you should use when calling secure endpoints.
console.log("Token2: ", credentials.access_token); //RETURNS THE TOKEN
return credentials.access_token;
},
function (err) {
console.error(err);
}
);
}

async function Token() {
const token = await GetAuthTokenAsync();
console.log("Token1: ", token); //RETURNS 'undefined'
return token;
}

Token().then(AuthToken => console.log('Token3: ', AuthToken));

In addition using .then inside an async function is allowed but it makes it harder to read. It can be simplified like this:

async function GetAuthTokenAsync() {
// Initialize the 2-legged OAuth2 client, set specific scopes and optionally set the `autoRefresh` parameter to true
// if you want the token to auto refresh
var autoRefresh = true; // or false

var oAuth2TwoLegged = new ForgeSDK.AuthClientTwoLegged(
FORGE_CLIENT_ID,
FORGE_CLIENT_SECRET,
["data:read", "data:write"],
autoRefresh
);

const credentials = await oAuth2TwoLegged.authenticate();
console.log("Token2: ", credentials.access_token);
//RETURNS THE TOKEN
return credentials.access_token;
}

If you really want to catch & console.log the error inside of GetAuthTokenAsync then you can add a .catch() function but it's better to let the error get thrown up the stack usually.

async/await logs to the console but returns no value and returns `Promise { pending }`

I'm simplifying quite a lot here, but it helps to understand. When you declare a function as async, what it does is wrap the function in a Promise. So, for example, this function :

async function something(){ return "hello world" }

can be read like :

function something(){
return new Promise((resolve, reject) => {
resolve("hello world")
})
}

In a nutshell, a Promise just schedules the execution of a function (in this case something) for later. So, we can only read the returned value later, when it's ready. When you call something(), it returns a Promise which is pending because it hasn't been executed yet (its execution is put on hold in a queue). You can learn more about how this works by reading about the event loop, the call stack and event queue.

To get the returned value of a Promise, we use a method called .then((value) => {}) which returns in the variable value the returned value of the function (in our case, value will contain "hello world"). So :

let r = something()
console.log(r) // Promise pending
r.then((value) => {
console.log(value) // "hello world"
})

If you want to use the value, you are gonna have to either :

  • Put your code inside the .then() and continue there
  • Put your code inside an async function and use await in front of the call
async function run(){
let value = await something()
console.log(value) // "hello world"
// do things with value
}

run()

Async function not returning value, but promise

async function myPromiseFunction() {
return Promise.resolve('hi')
}

(async () => {
console.log(await myPromiseFunction());
})()


Related Topics



Leave a reply



Submit