node.js catch block not firing as expected on await - node.js

My node process exited after logging
E callback: [Function: RP$callback],
E { serviceName: '....',
E errno: 'EAI_AGAIN',
E name: 'RequestError',
But I thought the below code would catch exceptions and return the defaulted value
var rp = require('request-promise');
async function verifyJWT(jwt: string): Promise<any> {
const userDataPromise = rp({
uri: ...,
method: 'POST',
json: {
...
}
});
const userData = await userDataPromise.catch((err: any) => {
console.error(`verifyJWT: err`, err)
return {
authInfo: {}
};
});
return userData;
}
export default verifyJWT
Are there certain types of failures that would not get caught here?
Would it be better to provide a rejection handler in catch on the promise?
Thanks,
Brent

You're mixing standard promise handling with async/await style promise handling. You should try to use one or the other, but not both. Using both methods simultaneously is not restricted, but it certainly adds confusion.
This is what async/await style promise handling should look like:
async function verifyJWT(jwt: string): Promise<any> {
try {
return await rp({
uri: ...,
method: 'POST',
json: {
...
}
});
} catch (err) {
console.error(`verifyJWT: err`, err);
return {
authInfo: {}
};
}
}
This is what standard promise handling should look like:
function verifyJWT(jwt: string): Promise<any> {
return rp({
uri: ...,
method: 'POST',
json: {
...
}
}).catch((err: any) => {
console.error(`verifyJWT: err`, err);
return {
authInfo: {}
};
});
}

You aren't using async await as it should be used
async await enables the code to wait for promise response and then move on to the next step. Any error returned by the rest endpoint, is caught by the catch block.
i've posted a sample code below, to show how async await would work with promise
var rp = require('request-promise');
var myfun = async ()=>{
let returnValue = null;
try{
let response = await rp("http://localhost:3000/getdata");
console.log("ajax success");
returnValue = response;
}
catch(err){
console.log("ajax error ",err.message)
returnValue = err.message;
}
return returnValue;
}
Note if you want to return any data from async function, then the calling function should also be async and await has to be used while invoking.
Read more about async await

Related

Response doesn't wait for async function

I found the aproach how to put the async function into a router endpoint and tried to implement it but not suceed. E.g. this link zellwk.com
The server sends only empty string instead of a huge string that is on snippet.host.
async function downloadData(url) {
const options = {
'method': 'GET', 'url': url, 'headers': {}, formData: {}
};
let result = '';
await request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body)
result = response.body
});
return {error: 0, text: result}
}
app.get('/token/:token', jsonParser, async function (req, res) {
console.log(req.params.token) //Don't mind this token
try {
let message = await downloadData('https://snippet.host/xgsh/raw')
res.send(message)
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});
How to make it wait for the download?
EDIT
I implemented node-fetch and got this:
{"error":0,"text":{"size":0}}
What did I wrong?
async function downloadData(url) {
const result = await fetch(url)
return {error: 0, text: result}
}
app.get('/token/:token', jsonParser, async function (req, res) {
console.log(req.params.token)
try {
let message = await downloadData('https://snippet.host/xgsh/raw')
res.send(message)
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});
in case of fetch call the response body need to be parsed first which you are missing.
async function downloadData(url) {
const res = await fetch(url)
if (res && res.status != 200)
throw new Error("status code other than 200 received")
let json = await res.json()
return {error: 0, text: json}
}
app.get('/token/:token', async function (req, res) {
console.log(req.params.token)
try {
let message = await downloadData('https://snippet.host/xgsh/raw')
// let json = await message.json()
res.send(message)
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});
i tried calling the api but every time it is returning 404 . so json cant be called in that case. but if 200 is returned then you call json and return that accordingly.
You can only usefully await a promise.
request doesn't return a promise, it takes a callback function instead.
This is one of the reasons that request was deprecated two years ago.
Replace request with something which supports promises such as node-fetch, the native fetch that was added to Node.js recently, or axios.
In case you must use request (eventhough it's deprecated); or in case you find a similar situation with a old function that doesn't return a promise; you can turn your request into a function that returns a promise.
Either write your own wrapper, in the form of
function requestPromise(options) {
return new Promise(
(resolve,reject) => request(options,
(err,response) => if (err) reject(err) else resolve(response)));
}
or, given that request's callback is in the form of function(err,response), directly use util.promisify
async function xxxx(...) {
try {
...
let response = await util.promisify(request)(options);
...
}
Because request isn't actually an async function that returns a Promise, you can create your own async version of request.
function asyncRequest(options) {
return new Promise((resolve, reject) => {
request(options, (err, response) => {
if (err) {
reject(err)
} else {
resolve(response)
}
})
})
}
Then in your project rewrite your downloadData function to be:
async function downloadData(url) {
const options = {
method: "GET",
url: url,
headers: {},
formData: {},
}
const result = await asyncRequest(options)
return { error: 0, text: result.body }
}

Await doesn't actually wait for an async function to complete

I'm starting to use Redis for caching in my NodeJS app. Unlike all the tutorials that can be found on the internet (for example this one: https://medium.com/geekculture/fasten-your-node-js-application-with-a-powerful-caching-mechanism-using-redis-fd76b8aa482f), I don't send the res just after calling the "cache" function. My "cache" function is a middleware returning a value if found.
Here is where the function is called:
const { requestLDAP } = require('../utils/ldap')
router.get('/:mode/:type/:code', async (req, res) => {
(...)
const resultLDAP = await requestLDAP(
type, params
);
console.log('---');
console.log(resultLDAP);
console.log('---');
And here is the part of the LDAP module:
async function requestLDAP(type, params) {
const key = JSON.stringify({type,params});
// checking the cache
await clientRedis.get(key, async (err, response) => {
if (response) {
console.log("[CACHE]");
return {
ok: true,
contenu: JSON.parse(response),
};
} else {
const simulated = Array.from({ length: 5 }, () => {
let r = { type: type.slice(0, -1) };
params.forEach((k) => {
r[k] = `${cle}_${Math.floor(100*Math.random(100))}`;
});
return r;
});
console.log("[API]");
await clientRedis.setex(cle, 10, JSON.stringify(simulated));
console.log('[SAVED]');
return {
ok: true,
contenu: simulated,
};
(...)
I don't write all the rest, which is basically all the actual LDAP call.
My problem is that the script doesn't seem to wait for the "await" to complete. Here is what I have in the console:
---
undefined
---
---
undefined
---
[CACHE]
[CACHE]
What am I missing here?
If I write console.log(response) I have the correct response, but once again AFTER expected.
[EDIT]
After helpful comments, here is how I wrote the function:
let finalResponse = await clientRedis.get(cle, async (err, response) => {
if (response) {
console.log("[CACHE]");
return response;
} else {
(...)
return {
ok: true,
contenu: finalResponse,
};
but finalResponse is a boolean, it doesn't return the real response.
As they said before, requestLDAP function returns nothing, not an actual Promise that you can wait for.
You need that requestLDAP returns a Promise because you are waiting for the result of clientRedis that you don't know when will you have it.
When you have it, you have to resolve or reject the Promise mentioned. A return here is not a return for requestLDAP, but for the caller of the callback.
Try packing and returning a promise like this:
async function requestLDAP(type, params) {
return new Promise( async (resolve,reject) => { // --> Promisify the asynchronous response
const key = JSON.stringify({type,params});
// checking the cache
await clientRedis.get(key, async (err, response) => {
if(err) reject('Something ugly happened here'); // --> Give a result for the promise in the future
if (response) {
console.log("[CACHE]");
resolve( { // --> Give a result for the promise in the future
ok: true,
contenu: JSON.parse(response),
} );
} else {
const simulated = Array.from({ length: 5 }, () => {
let r = { type: type.slice(0, -1) };
params.forEach((k) => {
r[k] = `${cle}_${Math.floor(100*Math.random(100))}`;
});
return r;
});
console.log("[API]");
await clientRedis.setex(cle, 10, JSON.stringify(simulated));
console.log('[SAVED]');
resolve( { // --> Give a result for the promise in the future
ok: true,
contenu: simulated,
} );
(...)
}
});
}
};

Await and .then not getting Axios Promise

I am tracing my way through an application i am building. I have numbered steps to figure out what is getting called and what is not.
I call Axios and try to call .then() to get the data from the promise that is returned, but it is never executed. the application just keeps on going:
module.exports = async function request(requestOpts = {}) {
console.log("5: in request... sending request")
const response = await sendRequest({
method: requestOpts.method,
url: requestOpts.url,
headers: requestOpts.headers,
}).then(function(response) {
console.log("7: in request.. returning response")
return response;
})
};
function sendRequest(requestOpts = {}) {
console.log("6: in sendRequest.. returning axios")
return (
axios({
method: requestOpts.method,
url: requestOpts.url,
headers: requestOpts.headers,
}).then((response) => {
console.log("Then ===> " + response)
return response;
}).catch((error) => {
console.log("Error ==> " + error)
return error;
})
);
}
Step 7 never shows up.. things just move on and complete. Have I got this set up correctly? Here is the calling module:
module.exports = async function axiosCaller() {
console.log('4: in axiosCaller')
const authHeader = buildBasicAuth();
let response = await request({
url: 'redacted',
method: 'get',
headers: {
authHeader
},
}).then((response) => {
console.log(response);
return handleResponse(response);
});
}
I think you should not return the async response in .then instead you need to return axios response as promise as
async sendRequest() {
return await axios(....)
}
and while calling sendRequest function you need to mention await infront of that or simply use .then to capture the response

How can I await data from a callback function and then return it?

I am in a asynchronous function, trying to return the data from a callback function:
async function getData(){
const client = new google.auth.JWT(
keys.client_email,
null,
keys.private_key,
['https://www.googleapis.com/auth/spreadsheets']
)
let returnData
const finalData = (data) => {
returnData = data
}
async function gsrun(client) {
const gsapi = google.sheets({version:'v4', auth: client})
const options = {
spreadsheetId: '1d3ZiP1I9jJ2ddlD1Hx2ylWn1VFD_5lYQ9Ps9e9gEqI',
range: 'Sheet1!A1:H5'
}
const data = await gsapi.spreadsheets.values.get(options)
return data.data.values
}
client.authorize( async (err, tokens)=> {
if(err) return console.log(err)
let data = await gsrun(client)
finalData(data)
})
return returnData
}
And in the console I get: Promise { undefined }. How should I await that promise to resolve or to await the data?
You are still trying to return before the data was assigned. You are not "waiting" until client.authorize has called the callback because you can't. You can only "return data from a callback" if the callback is called synchronously. But that's not the case here. It doesn't matter that you declared the callback as async, what matters is how/when the caller (i.e. client.authorize) calls the callback.
Wrap the client.authorize in a promise and await that in your getData function.
async function gsrun(client) {
const gsapi = google.sheets({
version: 'v4',
auth: client
})
const options = {
spreadsheetId: '1d3ZiP1I9jJ2ddlD1Hx2ylWn1VFD_5lYQ9Ps9e9gEqI',
range: 'Sheet1!A1:H5'
}
const data = await gsapi.spreadsheets.values.get(options)
return data.data.values
}
function authorize(client) {
return new Promise((resolve, reject) => {
client.authorize((err, tokens) => {
if (err) {
reject(err);
} else {
resolve(tokens);
}
});
});
}
async function getData() {
const client = new google.auth.JWT(
keys.client_email,
null,
keys.private_key, ['https://www.googleapis.com/auth/spreadsheets']
)
await authorize(client);
return await gsrun(client);
}
Here authorize will throw an error if the authentication failed and return the tokens if it is successful.
I wrote a post about callbacks and data. While it doesn't go into promises, maybe it can help your understanding of callbacks.
Your client.authorize is already awaiting gsrun() function to return a resolve (I am not sure how the rejection from gsrun() is handled but I suggest you use a .then() to handle resolve/reject if not implement a try,catch statement).
Another issue is that following code will still run despite you awaiting gsrun(client)
return returnData
because it is not inside the finalData() function. I suggest you only execute it is set.
Here's to show that returnData is returned before data is assigned to it
function returnPromise() {
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('Success')
},2000)
// reject('err')
})
}
async function test(){
returnPromise().then(
resolve=>{console.log(resolve)},
reject=>{console.log(reject)}
)
}
test()
console.log('I COME HERE FIRST') // Executed first before promise is returned
Here's a simple example of how you could implement rejection handling(for reference only)
function gsrun() {
return new Promise((resolve, reject) => {
//resolve('Success')
reject('err')
})
}
async function authorizeCallback(){
gsrun().then(
resolve=>{finalData(resolve)},
reject=>{console.log(reject)} // do error handling
)
}

Return from async function in hapi route

Using hapi v17
i have a route
{
method: 'GET',
path: '/redirectEbay',
handler: registerController.ebayRedirect
}
that leads to a controller
ebayRedirect: function ebayRedirect(request, reply) {
ebay.xmlRequest({
serviceName: 'Trading',
opType: 'GetSessionID',
appId: EBAY_CLIENT ,
devId: EBAY_DEV ,
certId: EBAY_SECRET ,
params: {
RuName: EBAY_RUNAME
}
},
function(error, data) {
console.log(data);
console.log(error);
sessionID = data.sessionID;
//catch ???
});
return (SessionID);
}
and then of course SessionID is undefined as its generated from an async function.
Attemp with async / await:
ebayRedirect: async function ebayRedirect(request, reply) {
const session = await ebay.xmlRequest({
...
params: {
RuName: EBAY_RUNAME
}
}, function(error, data) {
sessionID = data.sessionID;
return sessionID;
});
return (session);
}
It gives another error, looklike the whole handler is considered malformed because not returning someting ?
the async call is correct and returning the session
Debug: internal, implementation, error
Error: ebayRedirect method did not return a value, a promise, or throw an error
Another try with a different taste, still not resolving, like the await does not wait function to resolve as console.log is triggered immediately
At least got rid of the Error 500...
also tried a variation :
ebayS = async function() {
console.log ( ebay() );
gives
Promise { undefined }
The ebay.xmlRequest function uses a callback instead of a promise, so you have to wrap it in a promise:
ebayRedirect: function ebayRedirect(request, reply) {
return new Promise((resolve, reject) => ebay.xmlRequest({
params: {
RuName: EBAY_RUNAME
}
},
function(error, data) {
if (error) {
reject(error);
} else {
resolve(data.sessionID);
}
}
));
}

Resources