Response is send immediately and does not wait for await - javascript

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.

Related

Nodejs axios http request loop

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);
}))

JS Functions to make an API call

I have the following code.
I'm trying to make an API call (retrieve) passing since (obj.since), therefore, every time I make the call the API does not retrieve all data. However, so far, I haven't found the way to get since from the last record on my database.
var express = require("express");
var article = require("../models/article");
var request = require('request');
article.findOne({}, {since:1, _id:0}, { sort: { 'since' : -1 } }, function (err,obj) {
var **dataString** = `'{"consumer_key":"XXXXX", "access_token":"XXXXXXX", "since":"${obj.since}"}'`;
});
var options = {
url: 'https://xxxxxxxxx.com/v3/get',
method: 'POST',
headers: headers,
body: **dataString**
}
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
let package = JSON.parse(body);
for(var attributename in package.list){
var title = package.list[attributename]["given_title"] ;
var url = package.list[attributename]["given_url"] ;
var newArticle = {title: title, url: url, since: since}
article.create(newArticle, function(error, newlyCreated){
if(error){
console.log(error);
} else {
console.log(newlyCreated);
}
});
}
}
else {
console.log(error);
}
};;
request(options,callback)
How can I make an API call getting the obj.since from the database (MongoDB) and pass it to an object (options)?
You are doing async callback style operation in for loop which is causing this issue. I will change few things
Change findOne to have exec at the end so it returns promise
article.create already returns a promise if no callback specified.
Convert request to a promise style.
Use for..of loop to do async operation.
The code will look like this
var express = require("express");
var article = require("../models/article");
var request = require('request');
function hitApi(dataString) {
return new Promise((resolve, reject) => {
var options = {
url: 'https://xxxxxxxxx.com/v3/get',
method: 'POST',
headers: headers,
body: dataString
}
request(options, error, response, body => {
if (err) {
reject(err);
}
resolve(body);
});
});
}
async function perform() {
const dataString = await article.findOne({}, {since:1, _id:0}, { sort: { 'since' : -1 } }).exec();
const response = await hitApi(dataString);
const package = JSON.parse(response.body);
for (const attributename of package.list) {
var title = package.list[attributename]["given_title"] ;
var url = package.list[attributename]["given_url"] ;
var newArticle = {title: title, url: url, since: since}
const newlyCreated = await article.create(newArticle);
console.log(newlyCreated);
}
}
You can then call perform function. There might be few syntax error but you will get an idea.

Using data from an aysnc Javascript http request? (aws serverless)

I'm using the nodejs serverless module to create a lambda aws function.
'use strict';
const request = require('request');
const options = {
url: 'https://api.mysportsfeeds.com/v2.0/pull/nfl/2018-regular/games.json',
method: 'GET',
headers: {
"Authorization": "Basic " + Buffer.from("1da103"
+ ":" + "MYSPORTSFEEDS").toString('base64')
}
}
//this is automatically called by aws
module.exports.hello = async (event, context) => {
let result;
request.get(options, (error, response, body) => {
result = JSON.parse(body).lastUpdatedOn; //never happens cuz of async
});
return {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: result,
}),
};
};
The problem I'm having is that I can't return output from the get request, because the assignment to the result variable (in the async get request) happens after the return statement. I don't think I can turn the outer function into a callback function for the get request. How could I work around this?
An alternative could be to extract the request logic and put it into a new function.
Remember, you need to catch any errors, so use a try-catch block for doing that.
'use strict';
const request = require('request');
const options = {
url: 'https://api.mysportsfeeds.com/v2.0/pull/nfl/2018-regular/games.json',
method: 'GET',
headers: {
"Authorization": "Basic " + Buffer.from("1da103"
+ ":" + "MYSPORTSFEEDS").toString('base64')
}
};
function getResult() {
return new Promise(function (resolve, reject) {
request.get(options, (error, response, body) => {
if (error) return reject(error);
resolve(JSON.parse(body).lastUpdatedOn); //never happens cuz of async
});
});
}
//this is automatically called by aws
module.exports.hello = async (event, context) => {
let result = await getResult();
return {
statusCode: 200,
body: JSON.stringify({
message: 'Go Serverless v1.0! Your function executed successfully!',
input: result,
}),
};
};

How to capture the event in which all promises in the array are resolved?

I am using Bluebird library with NodeJS (with SailsJS framework)
Promise.all() is not capturing the event when all the promises in promises array are resolved.
What changes should be made in order to solve this problem?
var Promise = require("bluebird");
var request = require('request');
var http = require('http');
function searchMultiple(titles) {
var results = [];
return new Promise( function( resolveGlobal, rejectGlobal ){
var url = "http://xxx.xxx";
var promises = [];
titles.forEach(function (title, index) {
promises[index] = new Promise( function (resolve, reject) {
var data = {"x":title};
request({
uri: url,
method: "POST",
body : data
}, function(error, response, body) {
return resolve(body)
}
}
},
function (error, response, body) {
console.log("error");
return resolve();
}
);
})
})
Promise.all(promises).then(function(combinedResults) {
console.log("successfully resolved all promises");
return resolveGlobal(combinedResults);
}).catch(function (reason) {
console.log("error");
return rejectGlobal();
});
})
}
There is no need for you to return resolve(value), as it should only resolve the with the given result value.
There's also no reason to create a new promise in your searchMultiple function, since the Promise.all returns a promise. You should just return the promise you already have!
The resolveGlobal() is thus unnecessary and you can just return the result instead, since the then will wrap it as a resolved value.
All of your code can be rewritten as two very simple functions
function searchMultiple(titles) {
//Empty array of promises
var promises = [];
var url = "http://xxx.xxx";
//Get a promise for each title and push to array
titles.forEach(function(title){
promises.push(getData(title, url));
});
//Wait for all promises to resolve, and return the result
return Promise.all(promises)
.then(function(arrayOfResults){
//If this only shall return the array, this can be omitted aswell as the catch!
return arrayOfresults;
})
.catch(function(reason){
//Handle errors
});
}
function getData(title, url){
return new Promise(function(resolve, reject){
var data = {"x":title};
request({
uri: url,
method: "POST",
body : data
}, function(error, response, body) {
resolve(body)
}
}
},
function (error, response, body) {
console.log("error");
//Consider rejecting here instead since you'll get a result that is 'undefined'
resolve();
});
});
}
You should consider rejecting the promise in the error handler instead of resolving it with an undefined value. You might end up with errors further down the road if you get a result array back that have values that are undefined.
Try this:
var Promise = require('bluebird');
var request = require('request');
function searchMultiple(titles) {
return new Promise(function (resolveGlobal, rejectGlobal) {
var url = 'http://xxx.xxx';
var promises = [];
titles.forEach(function (title) {
promises
.push(new Promise(function (resolve, reject) {
var data = {
'x': title
};
request({
uri: url,
method: 'POST',
body: data
},
function (error, response, body) {
if (!error && response.statusCode == 200) {
return resolve(body);
}
return reject(error);
});
}));
});
Promise
.all(promises)
.then(function (combinedResults) {
console.log('successfully resolved all promises');
return resolveGlobal(combinedResults);
})
.catch(function (error) {
console.log('error');
return rejectGlobal(error);
});
});
}
And call function:
searchMultiple([...])
.then(function (results) {
console.log(results);
})
.catch(function (error) {
console.log(error);
});

Calling async functions recursively

I need to have an async method eg. getWeather called forever with a small delay between the success of previous call and beginning of the next call. I have used a recursive function for the purpose. I am concerned if this can cause a performance hit. Are there any better ways to do this?
var request = require('request');
var Promise = require('bluebird');
var delayTwoSecs = function() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, 2000);
});
};
var getWeather = function() {
return new Promise(function(resolve, reject) {
request({
method: 'GET',
uri: 'http://api.openweathermap.org/data/2.5/weather?lat=35&lon=139'
}, function(error, response, body) {
if (error) {
reject(error);
} else {
resolve(body)
}
});
});
};
var loopFetching = function() {
getWeather()
.then(function(response) {
console.log(response);
return delayTwoSecs();
}).then(function(response) {
loopFetching();
});
};
loopFetching();
You don't need the delayTwoSecs function, you can use the Promise.delay function.
Instead of getWeather, you can use the bluebird to Promisify all the functions and use the proper function, in this case getAsync, directly.
So, your program becomes like this
var Promise = require('bluebird');
var request = Promise.promisifyAll(require('request'));
var url = 'http://api.openweathermap.org/data/2.5/weather?lat=35&lon=139';
(function loopFetching() {
request.getAsync(url)
// We get the first element from the response array with `.get(0)`
.get(0)
// and the `body` property with `.get("body")`
.get("body")
.then(console.log.bind(console))
.delay(2000)
.then(loopFetching)
.catch(console.err.bind(console));
})();
This is called Immediately Invoking Function Expression.
setInterval() for recurring requests
You're over-complicating it with nested calls. Use setInterval() instead.
var request = require('request');
var Promise = require('bluebird');
var getWeather = function() {
return new Promise(function(resolve, reject) {
request({
method: 'GET',
uri: 'http://api.openweathermap.org/data/2.5/weather?lat=35&lon=139'
}, function(error, response, body) {
if (error) {
reject(error);
} else {
resolve(body)
}
});
});
};
var my_interval = setInterval("getWeather()",2000);

Categories

Resources