If I had to guess it's because jwtClient.authorize
accepts a callback instead of returning a promise -
async function getGoogleApiToken() {
let jwtClient = new google.auth.JWT(...);
await jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
console.log("Successfully connected!"+ tokens.access_token); //displaying the token correctly
return tokens.access_token; //returns the token // <- NO IT DOESN'T
}
});
}
Callbacks cannot return
a value. This is one of the most popular questions on StackOverflow. Callbacks like the ones used in jwtClient.authorize
are just a common usage pattern and do not interop with async
and await
directly. You must return a Promise
from your getGoogleApiToken
. One such way is to wrap a Promise around the jwtClient.authorize
call -
async function getGoogleApiToken() {
const jwtClient = new google.auth.JWT(...); // <-- btw use const
return new Promise((resolve, reject) => // <-- wrap in promise
jwtClient.authorize((err, tokens) => {
if (err)
reject(err) // <-- reject the error
else
resolve(tokens) // <-- resolve the result
})
)
}
We don't want to add this Promise wrapper every time we wish to use a callback-accepting function with other Promise-based code, including any async
functions. Turning any callback-accepting async function into a promise-returning async function can be a mechanical process. In fact, that's what NodeJS's util.promisify does. It works something like this -
// approximation of util.promisify
const promisify = f => (...args) =>
new Promise ((pass, fail) =>
f (...args, (err, result) => err ? fail(err) : pass(result))
)
// example callback-accepting function
const demoCallbackFunc = (a, b, callback) =>
setTimeout(callback, 1000, null, a + b)
// ordinary call
demoCallbackFunc(3, 5, (err, res) => {
if (err)
console.error(err)
else
console.log(res)
})
// promisified
promisify(demoCallbackFunc)(3, 5)
.then(console.log, console.error)
console.log("please wait 1 second...")
Using promisify
in your program will clean it up considerably -
async function getGoogleApiToken() {
const jwtClient = new google.auth.JWT(...);
const auth = promisify(jwtClient.authorize.bind(jwtClient))
return auth() // <-- no need for await, auth already returns a promise
}
Or without an intermediate assignment of auth
-
async function getGoogleApiToken() {
const jwtClient = new google.auth.JWT(...);
return promisify(jwtClient.authorize.bind(jwtClient))()
}
Or without bind
using an arrow function -
async function getGoogleApiToken() {
const jwtClient = new google.auth.JWT(...);
return promisify(callback => jwtClient.authorize(callback))()
}