I'm struggling with async await block. I have looked the multiple resources on the net but I just can't understand what I'm doing wrong here :
app.post('/api/my-api', async (req, res, next) => {
try {
filecontent = req.files.userFile.data.toString('utf8')
console.log("decoded file : ", filecontent);
let encoded_file = new Buffer(filecontent).toString('base64');
var apiClass = new RPC("http://localhost:1006", "my-api");
//the asynchronous function :
const answ = await apiMethod.call("api", [{"file" : encoded_file, "fileName":req.files.userFile.name}], res.json);
//the code I'd like to execute only after the previous function has finished :
console.log("answer : ", answ);
console.log("answering...");
res.json(answ);
} catch (err) {
console.log(err);
}
Apparently my console.log are executed before the the await line is done. I can tell because there's a console.log() in the asynchronous function too, and my res.json is sent before I receive answ.
How do I make sure the asynchronous function finishes before the rest of the code ?
Edit : here is the apiMethod.call function :
call(id, params) {
let options = {
url: this.url,
method: "post",
headers:
{
"content-type": "text/plain"
},
body: JSON.stringify( {"jsonrpc": "2.0", "id": id, "method": this.procedure, "params": params })
};
console.log(options);
request(options, (error, response, body) => {
if (error) {
console.error('An error has occurred: ', error);
} else {
console.log('Post successful: response: ', body);
}
});
}
Issue is in call function. Since it has async code (request call), it should be wrapped in promise which should resolve from callback function of request.
Updating call function to something like below should help:
function call(id, params) {
return new Promise((resolve, reject) => {
let options = {
url: this.url,
method: "post",
headers: {
"content-type": "text/plain",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: id,
method: this.procedure,
params: params,
}),
};
console.log(options);
request(options, (error, response, body) => {
if (error) {
console.error("An error has occurred: ", error);
reject(error);
} else {
console.log("Post successful: response: ", body);
resolve(body);
}
});
})
}
Related
ExampleObject is an example of what is expected from api endpoint
let ExampleObject={
"id":"",
"name":"",
"Body":""
}
how to make sure that the response has those keys without looping through the response? Response is a json object not an array
function handleError(err) {
//handling error code based
return err
}
export const GetUsers = async({
user,
Following
}, Paging) => {
try {
const response = await fetch(Apiurl, {
method: 'GET',
withCredentials: true,
credentials: 'include',
headers: {
'Authorization': Token,
'Content-Type': 'application/json'
}
})
if (response.status != 200) {
throw ('Error Sending request')
}
const ResponseArray = await response.json();
//Verify object here
if ('Key') {
throw ('error with json')
}
return ResponseArray
} catch (err) {
throw (handleError(err))
};
};
and then I call GetUsers from another module
async function callapi() {
try {
await GetUsers(Object, Paging)
} catch (err) {
}
}
You could use if ("id" in ExampleObject && "name" in ExampleObject && "Body" in ExampleObject) {}
I am trying to get the response data from 3 APIs which is depended on each other. when I call each external API the response has an array of external APIs, as I need all the 3 external APIs responses.
Here is the code:
const options = {
JSON: true,
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=UTF-8',
"Authorization": process.env.authID,
},
body: {},
uri: ''
};
app.post('/sync', async (req, res) => {
try {
let getFirstAPI = Object.assign({}, options);
getFirstAPI.uri = 'https://abcde'; // first API
request(getFirstAPI, function (err, httpResponse, body) {
Promise.all(body.projects.map((prct) => getVuln(prct))).then(rs => {
res.JSON({ message: rs });
});
});
}
});
async function getVuln(prodId) {
try {
let getSecondAPI= Object.assign({}, options);
getSecondAPI.uri = 'abcd' + prodId.id + '/abc'; //Second API
return await new Promise((resolve, reject) => {
request(getSecondAPI, function (err, httpResponse, body) {
let final = {
products: [],
dataList: []
}
final.products.push({
product_id: prodId.id,
product_name: prodId.abc,
product_ver: prodId.wdc
})
body.issues.forEach(csa => {
final.dataList.push({
product_id: prodId.id,
version: csa.Versions,
name: csa.Name,
pathJsonValue:await getPathfun(csa.link) //Here i am getting error "SyntaxError: Unexpected identifier"
});
});
resolve(final);
});
})
}
}
function getPathfun(urlStr) {
let getPathUrl = Object.assign({}, options);
getPathUrl.url = urlStr;
return new Promise((resolve, reject) => {
request(getPathUrl, function (err, httpResponse, body) {
resolve(body);
});
});
}
at the "pathJsonValue" I am getting an error while running the code, if I remove the await then "pathJsonValue" is empty. I have attached the error image below. please help me with this issue.
request(getSecondAPI, function (err, httpResponse, body) {
Callback must be an ASYNC function to have the possibility to use 'await' keyword inside it.
'await' keyword doesnt work in "forEach" method. Use for...of loop instead.
I have don't the API check if the token is expired. I have to make a GET call, if I got the 403, error from the API, then I should re-login.
I attempted:
app.get = async (body) => {
return new Promise((resolve, reject) => {
let user = await user.findOne({
where: {
accountId: body.accountId
}
});
if(user){
body.accessToken = user.accessToken;
} else {
body.accessToken = await app.login();
}
request(
{
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + body.accessToken
},
method: 'GET',
uri: `${config.acs.url}${body.url}`,
json: true
}
)
.then((response) => {
resolve(response);
})
.catch((error) => {
// logger.info(error);
if(error.statusCode == 403){
body.accessToken = await app.login(); <<------------- 🐞🐞🐞
app.get(body);
}
reject(error);
});
});
}
I don't know how else to avoid this error.
SyntaxError: await is only valid in an async function
I already have
app.get = async (body) => { ...
I need to re-login only when I get the 403 code in the error block.
How do I re-structure my code to achieve what I described?
The function used in the Promise is not an async function
Try this snippet
app.get = async (body) => {
let resolve, reject;
const promise = new Promise((re, rj) => {
resolve = re;
reject = rj;
});
let user = await user.findOne({
where: {
accountId: body.accountId
}
});
if(user){
body.accessToken = user.accessToken;
} else {
body.accessToken = await app.login();
}
request(
{
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer ' + body.accessToken
},
method: 'GET',
uri: `${config.acs.url}${body.url}`,
json: true
}
)
.then((response) => {
resolve(response);
})
.catch(async (error) => {
// logger.info(error);
if(error.statusCode == 403){
body.accessToken = await app.login(); <<------------- 🐞🐞🐞
app.get(body);
}
reject(error);
});
return promise;
}
I'm trying to throw an error from a function to another and to send a res.status(402). I put it in a try-catch block in the most basic way but still got the error.
: Unhandled promise rejection. This error originated either by throwing
inside of an async function without a catch block
this is the calling function:
const validateAction = async (req, res) => {
const { action, url, mail, orderId } = req.body;
try {
requestAction(action, url, mail, orderId);
} catch (err) {
console.error(err);
res.status(402).send({
status: 402,
error: "BUG",
});
}
this is the function I want from where I want to throw the error:
const requestAction = async (action, url, mail, oderId) => {
const result = actions.find((actionFind) => actionFind.action === action);
await axios({
//change the action to "add"
method: "post",
url: "XXXXXXXXXXXXXXXXXXXX",
data: {
key: "XXXXXXXXXXXXXXXXXXXXXXXX",
action: "XXXXX",
service: XXXXXX,
quantity: XXXXXXXXXX,
},
}).then(
(response) => {
if (response.data.error) {
sendLog(
` ${response.data.error} --- URL:${url} ACTION:${action} QUANTITY:${result.quantity} ID:${result.id}`,
"error"
);
// here is the throw that's not working---------------------------------------------
throw response.data.error;
//----------------------------------------------------------------------------------
} else {
sendMail(mail);
sendLog(
` Succesfully sent it --- URL:${url} ACTION:${action} QUANTITY:${result.quantity} ID:${result.id} To ${mail}`,
"info"
);
}
},
(error) => {}
);
};
I believe the answer is not complicated but I don't find it
You need to add an additional catch in the promise chain, for example like this:
const validateAction = async (req, res) => {
const { action, url, mail, orderId } = req.body;
requestAction(action, url, mail, orderId);
}
const requestAction = async (action, url, mail, oderId) => {
const result = actions.find((actionFind) => actionFind.action === action);
await axios({
//change the action to "add"
method: "post",
url: "XXXXXXXXXXXXXXXXXXXX",
data: {
key: "XXXXXXXXXXXXXXXXXXXXXXXX",
action: "XXXXX",
service: XXXXXX,
quantity: XXXXXXXXXX,
},
}).then(
(response) => {
if (response.data.error) {
sendLog(
` ${response.data.error} --- URL:${url} ACTION:${action} QUANTITY:${result.quantity} ID:${result.id}`,
"error"
);
throw response.data.error;
} else {
sendMail(mail);
sendLog(
` Succesfully sent it --- URL:${url} ACTION:${action} QUANTITY:${result.quantity} ID:${result.id} To ${mail}`,
"info"
);
}
}).catch(() => {
console.error(err);
res.status(402).send({
status: 402,
error: "BUG",
});
});
};
Basically need to add an
await So await requestAction(action, url, mail, orderId);
before the request action call.
Thanks to Felix Kling
1.How to write Promises Synchronously in Node so that I can get my desired output. I'm a newbie and would appreciate any help/suggestion.
// This is my core function
var compareData = function(userIdArray) {
return new Promise(function(resolve, reject) {
var missingArray = new Array();
userIdArray.forEach(function(id) {
var options = {
method: 'POST',
url: 'http://localhost:6006/test1',
headers:{
'content-type': 'application/json' },
body: { email: id },
json: true
};
request(options, function (error, response, body) {
missingArray.push(body);
});
});
resolve(missingArray);
});
}
//I'm calling my function here
compareData(userIdArray)
.then(function(missingArray){
console.log("The Body is: "+ missingArray);
});
/* I expect the console.log to print the missingArray with data from my POST call,
but it prints an empty array. Can someone please tell me how to do this synchronously.
I'm pretty new to Node and finding it difficult to understand.*/
If you don't want to use external libraries as per #Thomas answer, you can use native Promises directly - and it's not too much more verbose
var compareData = function compareData(userIdArray) {
return Promise.all(userIdArray.map(function (id) {
return new Promise(function (resolve, reject) {
var options = {
method: 'POST',
url: 'http://localhost:6006/test1',
headers: {
'content-type': 'application/json'
},
body: {
email: id
},
json: true
};
return request(options, function (error, response, body) {
error ? reject(error) : resolve(body);
});
});
}));
};
compareData(userIdArray)
.then(function (missingArray) {
console.log("The Body is: " + missingArray);
});
Or, as this is node, which can handle more modern code:
var compareData = userIdArray =>
Promise.all(userIdArray.map(id =>
new Promise((resolve, reject) =>
request({
method: 'POST',
url: 'http://localhost:6006/test1',
headers: {
'content-type': 'application/json'
},
body: {
email: id
},
json: true
}, (error, response, body) => error ? reject(error) : resolve(body))
)
));
compareData(userIdArray)
.then(missingArray =>
console.log("The Body is: "+ missingArray)
);
with bluebird and request-promise:
var Promise = require('bluebird');
var request = require('request-promise');
var compareData = function(userIdArray) {
//Promise.all():
//takes an array of promises (and/or values),
//returns a promise of the resolved array
return Promise.all(
userIdArray.map(function(id){
return request({
method: 'POST',
url: 'http://localhost:6006/test1',
headers: { 'content-type': 'application/json' },
body: { email: id },
json: true
});
})
);
}
is there anything that needs further explanation?
Without libraries, and assuming request doesn't already return a promise
var compareData = function(userIdArray) {
return Promise.all(
userIdArray.map(function(id) {
var options = {
method : 'POST',
url : 'http://localhost:6006/test1',
headers : { 'content-type': 'application/json' },
body : { email: id },
json : true
};
return new Promise(function(resolve, reject) {
request(options, function(error, response, body) {
if (error) {
reject();
} else {
resolve(body);
}
});
});
})
);
}
compareData(userIdArray).then(function(missingArray) {
console.log(missingArray);
});