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
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 am using nodeJS and mongoDB.
I coded a function to get the biggest index inside my database to be able to use it when I post a new element in the database.
I created a function to insert a element in my database :
function postComparison(){
console.log("Titre : " + document.getElementById("Titre").value);
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"_id": getLastIndice()+1, // HERE
"id_User": "2",
"PseudoUser": "bob",
"Titre" : document.getElementById("Titre").value
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://localhost:3000/comparaison", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));}
</script>
I am calling the getLastIndice() function to increment the index from the last index I have in my database.
The function getLastIndice() call the router :
function getLastIndice(){
console.log("on appel la fonction get LastIndice");
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
fetch("http://localhost:3000/comparaison/getLastIndice", requestOptions)
.then(response => response.text())
//.then(result => document.getElementById("textRepBDD").innerHTML = result)
.catch(error => console.log('error', error));
console.log('appel de la fonction qui recupere le dernier indice avec comme réponse : ' + response );
return parseInt(response);
}
</script>
And inside the router I have :
comparaisonRouter.route('/getLastIndice')
.get( (req, res, next) => {
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";
MongoClient.connect(url, function(err, db) {
if (err) throw err;
var dbo = db.db("siteComparaisonMax");
//Sort the result by name:
var sort = { _id: -1 }; // - 1 Pour trier dans un ordre decroissant.
dbo.collection("comparaisons").find().sort(sort).limit(1).toArray(function(err, result) {
if (err) throw err;
//console.log(JSON.parse(result._id));
console.log(result[0]._id);
res.json(result[0]._id);
db.close();
});
});
})
The function get last index is working but when I'm calling the postComparaison() function it just calling the getLastIndice() and that's all.
I think that I'm missing a next() somewhere.
To return a value from a promise you use it's .then(). The .then() method is not there for you to log the result. It is there for postComparaison() to call to get the result:
function getLastIndice(){
console.log("on appel la fonction get LastIndice");
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
// IMPORTANT! The `return` below in front of the `fetch()` is
// what will return the value to the caller:
return fetch("http://localhost:3000/comparaison/getLastIndice", requestOptions)
.then(response => response.text())
.then(result => {
console.log('code INSIDE THIS THEN executes AFTER we get response : ' + result );
return parseInt(result);
})
.catch(error => console.log('error', error));
console.log('code OUTSIDE THE THEN ABOVE executes BEFORE we get response');
}
This is very important. Code at the bottom of the function executes FIRST. Code inside the then executes LAST.
To clarify let's remove all details from the function:
function getLastIndice() {
// THIS HAPPENS FIRST
console.log('first');
return fetch()
.then(response => response.text())
.then(result => {
// THIS HAPPENS THIRD
console.log('third');
return parseInt(result);
});
// THIS HAPPENS SECOND
console.log('second');
}
The above code will log:
first
second
third
But please note:
function getLastIndice() {
return fetch() // THIS RETURN IS IMPORTANT ! !
.then(r => r.json())
.then(r => parseInt(r));
}
So basically a clean version of getLastIndice() can be written simply as:
function getLastIndice() {
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
return fetch("http://localhost:3000/comparaison/getLastIndice", requestOptions)
.then(x => x.text())
.then(y => parseInt(y));
}
That's all the function needs to be. We don't really need (or want) to catch the error here because we want the caller to be able to detect the error so we simply remove the .catch().
Now that the function works properly here's how you use it in postComparison():
function postComparison() {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
// Remember, .then() is how getLastIndice() RETURNS its value:
getLastIndice().then(indice => {
var raw = JSON.stringify({
"_id": indice+1, // WE GOT THIS FROM .then of getLastIndice()
"id_User": "2",
"PseudoUser": "bob",
"Titre" : document.getElementById("Titre").value
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("http://localhost:3000/comparaison", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
}
Alternatively you can return the variable raw and process the rest inside another .then(). That's the whole reason Promises were invented - to flatten callback nesting by chaining .then()s:
function postComparison() {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
// Remember, .then() is how getLastIndice() RETURNS its value:
getLastIndice()
.then(indice => {
var raw = JSON.stringify({
"_id": indice+1,
"id_User": "2",
"PseudoUser": "bob",
"Titre" : document.getElementById("Titre").value
});
return raw; // return this to the `then` below:
})
.then(requestBody =>
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: requestBody, // requestBody comes from raw above
redirect: 'follow'
};
return fetch("http://localhost:3000/comparaison", requestOptions);
})
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
// ^^ NOTE: all the then() are on the same level!
}
If you want postComparison() to return the final result just do the same thing we did with getLastIndice() above: add the return keyword in front of getLastIndice():
function postComparison() {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
return getLastIndice() // THIS RETURN IS IMPORTANT !
.then(indice => {
var raw = JSON.stringify({
"_id": indice+1,
"id_User": "2",
"PseudoUser": "bob",
"Titre" : document.getElementById("Titre").value
});
return raw;
})
.then(requestBody =>
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: requestBody, // requestBody comes from raw above
redirect: 'follow'
};
return fetch("http://localhost:3000/comparaison", requestOptions);
})
.then(response => response.text())
.then(result => {
console.log(result);
return result; // return the result into the chain of then()
// this return is ALSO IMPORTANT
})
.catch(error => console.log('error', error));
}
An Analogy:
Using Promises you ALWAYS have to remember that the code inside then() happens after the end of the function, not before! It actually makes sense if you think about it.
Say for example you are ordering a Pizza. What usually happens is something like this:
You call Restaurant() and order a pizza
pizzaDeliveryGuy(() => will send you the pizza)
You put down the phone
You wait for the pizzaDeliveryGuy()
Note that in the events above the pizzaDeliveryGuy() is the .then() and the Restaurant() makes a Prmoise to send you a pizza (using pizzaDeliveryGuy()). Even though the Restaurant() promised you to deliver the pizza BEFORE you put down the phone the send you the pizza event happens AFTER you put down the phone.
Your confusion is exactly this: not understanding that a promise happens in the future. The pizza delivery guy will send you the pizza after you make the order even though the restaurant promised you the pizza before you put down the phone. Similarly, Promises will execute the .then() callback after the end of the function, not before.
Modern code with async / await:
ECMAScript version 6 introduced two keywords that helps simplify handling asynchrnous code: async and await. They build upon Promise and is basically a special syntax for inlining promises.
Basically if you write something like this:
async function x () {
let y = await z();
console.log(y);
}
Javascript will compile it to something like this:
function x () {
z().then(y => console.log(y));
}
Using async/await we can simplify your code. First we can rewrite getLastIndice() like this:
async function getLastIndice() {
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
let response = await fetch("http://localhost:3000/comparaison/getLastIndice", requestOptions);
let txt = await response.text();
return parseInt(txt);
}
Because we are using await everything in the function above happens in sequence from top to bottom:
async function getLastIndice() {
// THIS HAPPENS FIRST
var requestOptions = {
method: 'GET',
redirect: 'follow'
};
// THIS HAPPENS SECOND
let response = await fetch(URL, requestOptions);
// THIS HAPPENS THIRD
let txt = await response.text();
// THIS HAPPENS LAST
return parseInt(txt);
}
Now we can rewrite postComparison() like this:
async function postComparison() {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
try {
let indice = await getLastIndice();
var raw = JSON.stringify({
"_id": indice+1,
"id_User": "2",
"PseudoUser": "bob",
"Titre" : document.getElementById("Titre").value
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw
redirect: 'follow'
};
let response = await fetch("http://localhost:3000/comparaison", requestOptions);
let result = await response.text())
console.log(result);
return result;
}
catch(error) {
console.log('error', error);
}
}
As you can see, async/await is much easier to read for simple sequential code like this. Just don't forget to await the results.
Still, even if you use async/await I think it's critical to understand how Promises work and how .then() work because async/await was built on top of promises. There are some situations where you want two or more things to run in parallel instead of sequentially and that is when you need to go back to promises and callbacks.
I have a function in my project that processes most of my API requests (see simplified draft below)
function sendRequest(){
var reqbody;
var resp;
reqbody = unimportantRandomFunction();
request.post({
headers: { 'content-type' : 'application/xml',
'accept' : 'application/xml'},
url: process.env.APIURL,
body: reqbody
}, function(error, response, body){
resp = convert.xml2js(body, {compact:true});
});
return resp;
}
The problem I am facing is that request.post is asynchronous and by the time I get the response, an undifined value has already been returned.
I tried experimenting with promises (something I am entirely new to) but obviously I can't place my return in a .then block either because that return will not belong to the sendRequest function (correct me if I'm wrong).
Any ideas where I could put my return statement to make it work, that is wait for the value of "resp" to be assigned?
How about this modification? Please think of this as just one of several possible answers.
Promise is used sendRequest(). And the response value is retrieved in the async function. At the following modified script, at first sample() is run. And sendRequest() is run, then the response value is retrieved at console.log(res).
Modified script:
function sendRequest() {
return new Promise((resolve, reject) => {
var reqbody;
var resp;
reqbody = unimportantRandomFunction();
request.post(
{
headers: {
"content-type": "application/xml",
accept: "application/xml"
},
url: process.env.APIURL,
body: reqbody
},
function(error, response, body) {
if (error) reject(error);
resp = convert.xml2js(body, { compact: true });
resolve(resp);
}
);
});
}
async function sample() {
const res = await sendRequest().catch(err => console.log(err));
console.log(res);
}
sample(); // This function is run.
References:
Promise
async function
If I misunderstood your question and this was not the direction you want, I apologize.
The chain I have is retrieving information from a Sql Server and then setting up the data to then be sent to an api via post. The problem is i receive this RequestError message, **"TypeError [ERR_INVALID_HTTP_TOKEN]: Header name must be a valid HTTP token ["key"]"
Objective
-Retrieve Data using id
-Format data
-Send new, separate request to an api with formatted data
-Resolve results returned from api call
router.js
router.get('/request/:id', controller.data_post)
module.exports = router
Controller.js
exports.data_post = function(req, res) {
...
RetrieveData( req.id ) //retrieves data
.then( results => { //format data
var obj = formatData(results);
let body = [];
body.push(obj);
return body //resolve formatted data
} //End Of Promise
})
.then( body => { //Send new, separate request to an api with formatted data
var options = :{
method: 'POST',
uri: 'url',
headers: {
'key':'value',
'Content-Type': 'application/json'
},
body: JSON.stringify(body),
json:true
}
return option
})
.then( results => {
//send results
})
.catch( error => {
//error routine
})
}
RetrieveData.js
function RetrieveData( id ){
const promise = new Promise((resolve, reject) => {
...
resolve(data)
}
return promise;
}
RequestUtility.js
const request = require('request-promise')
function requestutility(options) {
return request(options)
.then( response => {
return response;
})
.catch( error => {
return error;
})
}
Current Error
"name": "RequestError",
message": "TypeError [ERR_INVALID_HTTP_TOKEN]: Header name must be a valid HTTP token ["key"]",
options: Object{},
callback: function RP$callback(err, response, body) {
arguments:TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
caller:TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
}
Couple of problems I see here
You don't need to return Promise.resolve and Promise.reject in the request utility method. Since request promise returns a promise, your promise will be resolved when succesfull and rejected when error. So you can get rid of requestutility alltogether.
You are wrapping results in new Promise which is not required.
resolve( requestutility(option)) doesn't work the way you are expecting it to work as it will resolve to a promise instead of value.
Remove the key from headers.
I have tried to update the code. It should look like
const request = require("request-promise");
RetrieveData(id)
.then(results => {
const obj = formatData(results);
const body = [];
body.push(obj);
return body;
})
.then(body => {
const options = {
"method": "POST",
"uri": "url",
"headers": {
"key": "value",
"Content-Type": "application/json"
},
"body": JSON.stringify(body),
"json": true
};
return request(options);
})
.then(results => {
// send results
})
.catch(error => {
// error routine
});
When I'm trying to use "request" to access external API and get back the response in AWS Lambda, I don't know how to properly put my "return" code to return the response.
NodeJs 8.10
var request = require('request');
module.exports.sendcode = async (event) => {
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST'
};
return await request(options, function (error, response, body) {
console.log(body);
return {
statusCode: 200,
body: JSON.stringify({
message: body,
input: event,
}),
};
});
};
When I run this code in serverless framework, I got a null response, there is nothing in the body, actually it should have at least the "input" attribute.
But console.log already logs the actual response from API.
It looks like my "return" code is not executed at all.
(If I remove async and await, then the program hangs until timeout)
Can anyone help how to modify this code to make it work?
Request does not use promises, so your await keyword is doing nothing. If you want to use promises with request, you need to find a library that supports promises, as noted here: https://www.npmjs.com/package/request#promises--asyncawait A popular option is https://github.com/request/request-promise-native
However, it is a simple matter to simply wrap a request in a promise, so that you don't need to use another dependency
var request = require('request');
const requestHandler = (options) => new Promise((resolve, reject) => {
request(options, (error, response, body) => {
if (error) {
console.error(error);
reject(error);
} else {
console.log(response, body);
resolve({ response, body });
}
});
});
module.exports.sendcode = async (event) => {
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST'
};
const { response, body } = await requestHandler(options);
return {
statusCode: 200,
body: JSON.stringify({
message: body,
input: event,
})
}
};
Instead of writing your own promise wrapper, maybe request-promise pacakge with promise wrapper on top of request by the same authors, would be of interest to you.
const rp = require('request-promise')
module.exports.sendcode = async (event) => {
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST'
};
const body = await rp(options)
console.log(body);
return {
statusCode: 200,
body: JSON.stringify({
message: body,
input: event,
}),
};
};
If you want full response you've to simply set resolveWithFullResponse to true
let options = {
url: 'https://api.netease.im/sms/sendcode.action?' + content,
method: 'POST',
resolveWithFullResponse: true
};
// now response contains full respose
const response = await rp(options)
// response.body
// response.statusCode
Use Async/Await with promises. You cannot use Async-Await syntax with callback. Either you must write a promise wrapper for your 'request'callback or you can use request-promise module which is already available.
I have already provided answer for similar problem which you can refer here.
Undefined value after returning an array of values from a MySQL query in a different file