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);
}
}
));
}
Related
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 }
}
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,
} );
(...)
}
});
}
};
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
I am using the libraries mongoose 5.4.20, request 2.88.0, q 1.5.1, and deasync 0.1.14.
When using .cursor() and .eachAsync() the promises don't get resolved in the order I would expect. In particular, I make a request to the data base, and for each returned document, I make several external requests via an async series. The sample code is as follows:
Request Promise:
function Request (options) {
const result = Q.defer();
request(options, function (error, response, body) {
if (error) {
result.reject(error);
} else {
result.resolve(body);
}
});
return result.promise;
}
Async Series Promise:
function Series (entry) {
const result = Q.defer();
const errors = [];
//Iterate over some given array
async.eachSeries(array, function (doc, cb) {
//Make some async request
return Request(doc)
.then(function (body) {
//do stuff with 'body' and 'entry'
})
.then(function () {
//Move to the next doc in 'array'
return cb(null);
})
.fail(function (err) {
//Handle errors
errors.push(err);
//Move to the next doc in 'array'
return cb(null);
});
}, function (err, results) {
if (err) {
errors.push(err);
}
if (errors.length !== 0) {
result.reject(errors);
} else {
console.log('A');
result.resolve();
}
});
return result.promise;
}
Mongoose:
mongooseModel
.find()
.cursor()
.eachAsync(entry => Series(entry))
.then(function () {
console.log('B');
}, function (err) {
console.log(err);
});
What confuses me is that the final callback in .then() after .eachAsync() seems to be called before the promise in .eachAsync() is resolved, i.e. 'console.log('B')' is called before 'console.log('A')'. I would have expected, that .eachAsync() will wait until the promise is resolved, then pulls the next document from the collection and so on, and only at the very end, when all promises are resolved, the final .then() is called.
Any suggestion as to what I am doing wrong would be greatly appreciated. Thanks.
I need help with ES6 Promises chaining in array processing.
How to process/define each item of array which goes into Promise.all method, when there is other async method inside resolve?
Here is simplified example:
function getData(data, callback) {
let groupPromises = data.map(row => {
var coordinates = getCoordinates(row);
return Promise.resolve({
"place": getPlaces(coordinates), //how to invoke this method
"data": row
};
});
Promise.all(groupPromises)
.then(groups => callback(groups))
.catch(err => console.log(err));
}
}
function getPlaces(coordinates) {
return new Promise(function(resolve, reject) {
if(coordinates == null) {
reject();
}
parameters = {
location: [coordinates.latitude, coordinates.longitude],
rankby: "distance",
};
googlePlaces.searchPlace(parameters, function (error, response) {
if (error) {
reject(error);
};
resolve(response);
});
}
}
You can do it like this where you add a .then() handler to your first promise that gets the place and then when that's available returns the object you want. The resolved results of your Promise.all() will then be the array of objects you want:
function getData(data, callback) {
let groupPromises = data.map(row => {
var coordinates = getCoordinates(row);
// add .then() handler here to convert the place result
// into the object you want it in
return getPlaces(coordinates).then(place => {
return {place: place, data: row};
});
});
return Promise.all(groupPromises)
.then(groups => callback(groups))
.catch(err => {
console.log(err);
throw err;
});
}
}
function getPlaces(coordinates) {
return new Promise(function(resolve, reject) {
if(coordinates == null) {
reject();
}
parameters = {
location: [coordinates.latitude, coordinates.longitude],
rankby: "distance",
};
googlePlaces.searchPlace(parameters, function (error, response) {
if (error) {
reject(error);
};
resolve(response);
});
}
}
FYI, since you're converting over to promises, why not just return the promise from getData() and not use a callback there at all? Your current code has no way of communicating back an error from getData() which is something that comes largely for free with promises.
In fact with pure promises, getData() could be simplified to this:
function getData(data, callback) {
return Promise.all(data.map(row => {
return getPlaces(getCoordinates(row)).then(function(place) {
return {place: place, data: row};
});
}));
}