Nodejs axios http request loop - javascript

I've a problem with Axios and foreach loop. My Api provider support only 5 contemporary call so I want call one by one but when execute this code the each body not wait the the finish call function and receive the error code 429. How can resolve this? thanks.
async function call(url) {
var options = {
method: 'GET',
url: url,
auth: {
username: '*****',
password: '*****'
}
};
var response = await axios.request(options);
print(response.data["Id"])
}
app.get('/save', async (req, res) => {
var options = {
method: 'GET',
url: 'getListUser',
auth: {
username: '***',
password: '***'
}
};
var response = await axios.request(options);
response.data["users"].forEach( async (val) => {
console.log("ENTER");
var url = 'getDetailUser' + val["id"];
var res = await call(url); // <- How to wait finish this?
console.log("EXIT")
}, (err) => {
console.log(err)
})
res.status(200).send("ok").end();
});

FYI, Promise couldn't work with loop that involves callback ie forEach. Alternatively, you could use for of
try {
for (const val of response.data['users']) {
console.log("ENTER");
var url = 'getDetailUser' + val["id"];
var res = await call(url);
console.log("EXIT")
}
} catch (error) {
console.log(error)
}

I would say answer by #Ifaruki is correct with a minor change
await Promise.allSettled(response.data["users"].map(val => {
var url = 'getDetailUser' + val["id"];
return call(url);
}))
For details check the difference. In some cases Promise.all might work but if any of the Promise fails, the whole result of Promise.all will be a rejection.
The code 429 can be resolved by looking at 429 Too Many Requests

Promise.all() is the way
await Promise.all(response.data["users"].map(val => {
var url = 'getDetailUser' + val["id"];
return call(url);
}))

Related

Response is send immediately and does not wait for await

I want to wait for the getAllData function to be finished, but that does not work.
let _partnerToken;
async function getAllData(dealerId,id){
let partnerToken;
var options = {
'method': 'GET',
'url': _url + '/' + dealerId,
};
request.get(options, async function (error, response) {
if (error) throw new Error(error);
partnerToken = response.body.slice(17,-2);
await getCarData(partnerToken,id);
await getCarImages(partnerToken, id);
_partnerToken = partnerToken;
})
}
Here the post request where I want to call this function and send a response when the data is all loaded and the function is finished.
app.post('/api/urlData', async function(req,res){
const str = CircularJSON.stringify(req);
await getAllData(req.body.dealerId, req.body.vin);
res.send(req)
});
The problem is that it does the response immediately, so there is no data for the frontend.
My goal is to send a signal with the response to my frontend, that all the data has loaded and can then be displayed.
async functions always must return a Promise. If you don't return a Promise, the runtime implicitly creates and returns one resolving to your return value (in your case undefined).
The reason why the call to api/urlData happens immediately without waiting for the other requests to be completed in getAllData() is that you're calling request.get(...) passing a callback function. This function works asynchronously, but for the runtime it's an ordinary synchronous function call.
To solve this problem you have to create and return a Promise manually in getAllData() and call the resolve() callback after all requests have been completed:
async function getAllData(dealerId, id) {
return new Promise<any>((resolve, reject) => {
const options = {
'method': 'GET',
'url': _url + '/' + dealerId,
};
request.get(options, async function (error, response) {
if (error) {
reject(error);
return;
}
let partnerToken = response.body.slice(17,-2);
await getCarData(partnerToken,id);
await getCarImages(partnerToken, id);
_partnerToken = partnerToken;
resolve(returnValue); // <--- your return value
});
});
}
await getAllData(1, 2)
Solution
I started working with Promises and now the the responds waits for the functions to be finished.
Here is one of the new methods:
async function getCarData(partner, id) {
const options = {
url: url + '/?partnertoken=' + partner + '&fahrzeug_id=' + id,
headers: {
'x-api-key': key
}
};
return new Promise(function (resolve, reject){
request.get(options, function (error, response, body) {
if (!error && response.statusCode == 200) {
let res = JSON.parse(body);
resolve(res.data[0]);
}else
reject(error);
});
})
}
app.post('/api/urlData', async function (req, res) {
let vin = req.body.vin;
let dealerId = req.body.dealerId;
const str = CircularJSON.stringify(req);
_partnerToken = await getPartnerToken(dealerId);
_data = await getCarData(_partnerToken,vin);
_dataImage = await getCarImages(_partnerToken,vin);
res.send(str)
});
I resturctured it so that every of the three methods returns a Promise.

Wait for an Axios call to finish in Quasar / Vue

I have a function in a Quasar application that needs to call an Web API that I need to wait until I get the results back to call the next function. I've posted my code and I'm sure that I'm missing some await or async or have them in the wrong place.
Global ex='';
testFunction3(arg){
console.log(arg)
} ,
testFunction2(){
this.ex = update_Members_data_term("#musc.edu");
},
async testFunction(){
await this.testFunction2()
this.testFunction3(this.ex)
},
API call:
function update_Members_data_term(term) {
axios.get(
'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi', {
params: {
db: 'pubmed',
api_key: '',
retmode: 'json',
retmax: 200,
term: term
}
}
).then(async response => {
return response.data.esearchresult.idlist;
}).catch(error => {
console.log(error.response)
})
}
Thanks for the help.
testFunction2 Doesn't return a Promise so await does nothing. You are actually doing await null;
So the fix will be:
function update_Members_data_term(term) {
return new Promise((resolve,reject) => {
axios.get(
'https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi', {
params: {
db: 'pubmed',
api_key: '',
retmode: 'json',
retmax: 200,
term: term
}
}
).then(async response => {
resolve(response.data.esearchresult.idlist);
}).catch(error => {
reject(error.response)
})
});
}
async testFunction2(){
this.ex = await update_Members_data_term("email_here");
},
Note that you might just await axios.get instead of returning a Promise, but I'm not sure what the syntax is.

Resolve promises created within Array.map on a timeout

I'm trying to resolve an array of promises using a timeout to avoid rate limit exceptions on API requests. However, I'm still hitting the rate limit, as the timeout doesn't seem to be working.
Not really sure how to make this work.
router.route("/").put(async (req, res) => {
const { apiKey: access_token, course, assignments } = req.body;
try {
const returnedIds = [];
const promises = await assignments.map((assignment) => {
return axios({
method: "PUT",
url: `https://url.to.api/api/v1/courses/${course}/assignments/${assignment.id}`,
params: {
access_token,
},
data: {
assignment: {
points_possible: assignment.points_possible,
},
},
});
});
const promiseResolution = function() {
Promise.all([...promises]).then((values) => {
values.forEach((_, index) => {
returnedIds.push(assignments[index].id);
});
res.status(201).json({
returnedIds,
});
});
};
setTimeout(promiseResolution, 5000);
} catch (e) {
res.status(401);
}
});
If you just want to put some time between API calls this should do.
router.route("/").put(async (req, res) => {
const { apiKey, course, assignments } = req.body;
try {
const returnedIds = [];
for (const assignment of assignments) {
returnedIds.push(await loadID(apiKey, course, assignment));
await wait(5000);
}
res.status(201).json({ returnedIds })
} catch (e) {
res.status(401);
}
});
function wait(duration) {
return new Promise((resolve) => setTimeout(resolve, duration));
}
function loadID(apiKey, course, assignment) {
// Makes the request, parses out the stuff you want from the response...
}
I would caution against using Promise.all since you probably want to check the result of each request before making the next one. For example, if the third request gets rate limited, you probably shouldn't bother making further requests.
It's because Promise.all will fire all promises at once, so your setTimeout is just set a timeout for all promises not individual promise as well.
You should try to make delay for each promise:
const promises = await assignments.map((assignment) => {
// some delay function
return axios({
method: "PUT",
url: `https://url.to.api/api/v1/courses/${course}/assignments/${assignment.id}`,
params: {
access_token,
},
data: {
assignment: {
points_possible: assignment.points_possible,
},
},
});
});
You can try this: (It's React but you should only focus on fetchData function) and see the logs:
https://codesandbox.io/s/zen-feynman-ql833?file=/src/App.js

Nodejs https.request head method issue

Have a problem with nodejs, https.request returns <http.ClientRequest>
const checkStatus = await https
.request(
{
method: 'HEAD',
host: 'host',
path: 'path',
},
(response) => {
const { statusCode } = response;
// idk
},
)
.on('error', (e) => {
if (e) {
throw new Error();
}
})
.end();
Can i somehow return statusCode instead <http.ClientRequest> inside checkStatus variable?
You can only usefully await a promise but https.request does not return a promise.
Either:
Wrap it in a new Promise or
Replace it with a library which supports promises by default (such as Axios or node-fetch)
(async function () {
const url = "https://jsonplaceholder.typicode.com/todos/1";
const response = await axios.head(url);
console.log(response.status);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>

JS/NodeJS Problem with loop inside async function

I have a problem with this piece of code:
async getDomains (req, res) {
try {
let domains = await Domain.findAll({ raw: true })
for(domain of domains) {
console.log('1')
var options = {
host: domain.name,
port: 443,
method: 'GET'
};
var request = https.request(options, (res) => {
console.log('2')
console.log('iam here')
domain.ssl = {
'valid_until': res.connection.getPeerCertificate().valid_from
}
});
console.log('3')
request.end();
}
console.log('4')
res.send(domains)
} catch(err) {
res.status(400).send({
error: err
})
}
},
The output should be 1, 2, 3, 4 but instead I got 1, 3, 4, 2.
Does anyone have an idea how to achieve that?
You've provided a callback to request, so it's going to send the request and move on, only logging 2 once a response is received. You'll want to use some sort of Promise or async/await to wait for the response from your request.
var request = await (new Promise((resolve, reject) => {
https.request(options, (res) => {
console.log('2');
console.log('i am here');
domain.ssl = { /* stuff */ };
resolve();
});
));

Categories

Resources