This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I am trying to call a GET API under three different configurations for region parameter, and combine the results into a single result object which I want the API to return as a JSON.
Here is my approach, which uses an array of Promises.
app.get("/api/v2/leagues/games", (req, res) => {
const regions = ['uk', 'us', 'au'];
let result = {};
let promiseArray = regions.map(region => {
return new Promise((resolve, reject) => {
const uri = `https://api.the-odds-api.com/v3/odds/?apiKey=${functions.config().theoddsapi.key}&sport=${req.query.league}®ion=${region}&mkt=${req.query.mkt}`;
console.log(uri);
request.get(uri, (error, response, body) => {
if (body) {
result[region] = JSON.parse(body).data;
resolve(body);
}
else {
console.error("error:", error);
console.log("statusCode:", response && response.statusCode);
reject(error);
}
});
});
});
Promise.all(promiseArray).then(() => {
return res.json(result)
}).catch(() => response.errorCode);
});
});
The approach you are using it correct, however you have made a little mistake.
Though I have not tested it but the following code should do what you want it to do.
app.get("/api/v1/leagues/games", (req, res) => {
const promiseRequest = (sport, region, mkt) => {
return new Promise(resolve => {
const theOddsApiUrl = `https://api.the-odds-api.com/v3/odds/?apiKey=${functions.config().theoddsapi.key}&sport=${sport}®ion=${region}&mkt=${mkt}`;
request.get(theOddsApiUrl, (error, response, body) => {
if (body) {
resolve(body)
}
});
})
}
var sport = req.query.sport;
var mkt = req.query.mkt;
let allRegionsOdds = {};
Promise.all([
promiseRequest(sport, 'uk', mkt),
promiseRequest(sport, 'us', mkt),
promiseRequest(sport, 'au', mkt)
]).then(body => {
var response = allRegionsOdds[region] = body; // you can deide if this assignment is necessary or not for you
res.json(response); // You want to return the response for the main Get after all the promises have been fulfilled.
}).catch(); // You can handle special cases in the catch, forexample when some promises fail.
//if you return the json at the end of function, your Promise.all hasn't gotten the time to be complete yet
//and hence the variable response is still empty
//res.json(response);
});
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 17 days ago.
I am using the mssql npm library which is working, but I am struggling to get the recordset returned from the sub-level callback.
How do I get the recordset back when it's multiple layers of callback?
let storedRecordset = await doQuery();
async function doQuery(){
let recordset;
const ps = new sql.PreparedStatement(/* [pool] */)
ps.input('param', sql.Int)
ps.prepare('select #param as value', err => {
// ... error checks
ps.execute({param: 12345}, (err, result) => {
// ... error checks
recordset = result;
// release the connection after queries are executed
ps.unprepare(err => {
// ... error checks
return recordset;
})
})
})
}
Thanks
let storedRecordset = await doQuery();
function doQuery() {
return new Promise((r) => {
let recordset;
const ps = new sql.PreparedStatement(/* [pool] */);
ps.input("param", sql.Int);
ps.prepare("select #param as value", (err) => {
// ... error checks
ps.execute({ param: 12345 }, (err, result) => {
// ... error checks
recordset = result;
// release the connection after queries are executed
ps.unprepare((err) => {
// ... error checks
r(recordset);
});
});
});
});
}
I have developed a code to fetch the image information of an object inside a loop statement. However, when I print the output at the bottom of the loop, it is empty. Can anyone help me with this, please? The getMediaInfo function is an Axios call.
const postsWithImageURLS = [];
res.data.forEach(async (post) => {
const response = await getMediaInfo(post.featured_media);
postsWithImageURLS.push({...post, featured_url: response});
});
console.log(postsWithImageURLS);
Promise.all(res.data.map(async (post) => {
if (post.categories.includes(NEWS_CATEGORY_ID)) {
const response = await getMediaInfo(post.featured_media);
post = {...post, featured_url: response};
return post;
}
})).then(postsWithImageURLS => console.log(postsWithImageURLS));
You should access postsWithImageURLS after all async methods finish.
I don't know exact content of the getMediaInfo function. But if it doesn't return a promise you can't use await before calling it.
Check this out:
const getMediaInfo = (data) => {
return new Promise(async (resolve, reject) => {
try {
let response = await axios.get(data); // data must be a url ofc
resolve(response); // Resolve must get a data you want to get when you call getMediaInfo
} catch (error) {
reject(error);
}
});
}
const postsWithImageURLS = [];
res.data.forEach(async (post) => {
const response = await getMediaInfo(post.featured_media);
postsWithImageURLS.push({...post, featured_url: response});
});
console.log(postsWithImageURLS);
I am trying to create a function with a GET request that returns a portion of the data from the GET request. However, it keeps returning before the data is retrieved, so I keep getting "undefined". How can I set this up so it actually waits for the data to be set before returning?
let getInfo = async () => {
const request = net.request({
url: URL
})
return new Promise((resolve, reject) => { // Promise being here DOES work
request.on('response', (response) => {
response.on('data', (chunk) => {
//return new Promise((resolve, reject) => { //Promise being here does NOT work
let body = JSON.parse(chunk)
let info = body.data
if (info){
resolve(info);
}
reject();
//})
});
});
request.write('')
request.end()
}).then(data => {
console.log("From then: "+data)
return data
})
}
getInfo().then(data => {
console.log("From outside: "+data)
})
Edit: This is the updated version that still does not work. I am trying to use the native electron method and I don't see why this doesn't work. The "From then:" part displays the info correctly. But when run "From outside:" it prints undefined. Does the issue have anything to do with the response.on being nested inside the request.on?
Solution: As #NidhinDavid showed in his answer, the issue was that the promise was inside the 'response' listener. Moving the 'GET' request from start to finish inside the Promise fixed it to giving the correct output. I have updated my code to reflect that for future individuals.
let getInfo = () => {
let info;
const request = net.request({
url: URL
})
return new Promise((resolve, reject) => {
request.on('response', (response) => {
response.on('data', (chunk) => {
request.write('')
request.end()
let body = JSON.parse(chunk)
info = body.data
if (info) {
resolve(info)
} else {
reject('Something went wrong');
}
});
});
})
}
getInfo()
.then(data => {
// this will be your info object
console.log(data)
})
.catch(err => {
// this will log 'Something went wrong' in case of any error
console.log(err)
})
You need to return inside your, on type event handler. Read more about asynchronous code and synchronous code here
I couldn't find the net module and the one which is included with Nodejs do not have request method. So to get the similar concept of event emiters and promise I am using http module and doing a http request to fetch json and parse it
'use strict'
var https = require('https');
const getInfo = async () => {
// create a new promise chain
// remember it is a chain, if one return is omitted
// then the chain is broken
return new Promise((resolve, reject) => {
var options = {
host: 'support.oneskyapp.com',
path: '/hc/en-us/article_attachments/202761727/example_2.json'
};
// start the request
https.request(options, function (response) {
var str = '';
// data arrives in chunks
// chunks needs to be stitched together before parsing
response.on('data', function (chunk) {
str += chunk;
});
// response body obtained
// resolve (aka return) the result
// or parse it, or do whatever you want with it
response.on('end', function () {
resolve(str)
});
// errors are another event
// listen for errors and reject when they are encountered
response.on('error', function (err) {
reject(err)
})
}).end()
})
}
//*********************************************
// using async await
//*********************************************
// if this is the entry point into app
// then top-level async approach required
(async ()=>{
try{
let data = await getInfo()
console.log("From ASYNC AWAIT ")
console.log(JSON.stringify(JSON.parse(data)))
}
catch (err) {
console.log("operation failed, error: ", err)
}
})();
//************************************************
// using promise chains
//************************************************
getInfo()
.then((data)=>{
console.log("FROM PROMISE CHAIN ")
console.log(JSON.stringify(JSON.parse(data)))
})
.catch((err)=>{
console.log("operation failed, error: ", err)
})
Tyr this, it might works for you,
let info;
const getInfo = async (_url)=>{
const response = await fetch(_url);
const data = await response.json();
info = data;
} ;
const url = "some url";
getInfo(url);
console.log(info);
Async function always returns a promise, so either consume that promise or internally await the data and assign it to some variable.
Check for the valid data required in info by logging it to the console.
Im doing an OpenWeatherMap project with the task of grabbing the temperatures from the API call. I cannot figure out how to pass the temperature values from my 'for' loop to my 'temperature' array that I've defined at the top:
const request = require('request');
let apiKey = 'xxxxxxxxx';
let url = `http://api.openweathermap.org/data/2.5/forecast?lat=30.2240897&lon=-92.0198427000000&units=imperial&${apiKey}`
let temperature = new Array();
request(url, function (err, response, body) {
if(err){
console.log('error:', error);
} else {
let data = JSON.parse(body);
let weatherList = data.list;
for (i=0; i<weatherList.length; i++) {
temperature[i] = weatherList[i].main.temp;
}
return temperature;
}
});
Any help on how to push the values I've gathered from my for loop to the 'temperature' array would be so helpful!
Since request() is an async call and your further processing depends on the value of temperature array, you need the code to access it, inside that request callback.
I'm suspecting you might be immediately accessing the temperature array after the async block of code which is call to request function.
var arr = new Array();
setTimeout(function(){
arr.push(10);
console.log("inside async block");
console.log(arr);
}, 1000);
console.log("immediately after async block");
console.log(arr);
The api calls using ajax or fetch whatever method you are using, takes time to return response from server. Since you are making an api call, which is asynchronous and you are trying to assign temperature value before ajax call is finished. That is the reason temperature is always empty. You need to use some callback to access temperature value from inside api response.
Like this:
request(url, function (err, response, body) {
if(err){
console.log('error:', error);
} else {
let data = JSON.parse(body);
let weatherList = data.list;
for (i=0; i<weatherList.length; i++) {
temperature[i] = weatherList[i].main.temp;
}
useTemp(temperature);
}
});
useTemp(temperature)
{
//Do something with temperature
}
I think you have a few things going on here that are holding you back.
First: Request is not a Promise based library, as such we need to wait on the response to return. We need to create a Promise for us to wait for.
Second: ES6 brings us easy ways to make this readable and repeatable.
Below are a few examples of your exact call but done in different methods:
/**
* Must await for a Promise, request does not natively return a Promise, so create one
* */
const request = require('request');
let temperature = new Array();
module.exports.getWeatherRequest = async () => {
// Create Our Returned Promise
function doRequest(url) {
return new Promise((resolve, reject) => {
request(url, function (error, res, body) {
if (error) {
reject(error)
}
resolve(JSON.parse(body));
})
})
}
/*
* Call Our Promise
**/
let resp = await doRequest(url);
/*
* For/of Loops through Object for only temps
* Push each value into temperature
**/
for (let temps of resp.list) {
temperature.push(temps.main.temp)
}
/*
* Outcome
*/
console.log('Temps : ', temperature)
}
ES6 and with Node-Fetch:
const fetch = require('node-fetch');
module.exports.getWeatherFetch = async () => {
let weather = await fetch(url)
.then(blob => blob.json())
.then(data => data)
.catch(error => { console.error(error) })
console.log('Weather Response ', weather)
// Here you'll handle the data/for of loop
}
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I have a function that makes a database query, then needs to return the result.
The query is to a mysql database, using Node.js, the result is then returned to an Inquirer (NPM module) prompt.
If this was a front end issue, I would use the built in promises of jquery: (example). $.ajax.done(). However the mysql NPM package doesn't have built in promises for the query() method.
// OPTION 1, wait for query to complete before returning choicesArray (this example returns an empty array)
choices() {
let choicesArray = [];
connection.query(`SELECT * FROM products`, (err, res)=>{
for (item of res) {
choicesArray.push(`${item.product} | ${item.price}`);
};
});
// wait here for query to complete
return choicesArray;
}
// OPTION 2, change the syntax to take advantage of the query callback, something more like below (this example does not return the choicesArray all the way to the enclosing function)
choices() {
connection.query(`SELECT * FROM products`, (err, res)=>{
let choicesArray = [];
for (item of res) {
choicesArray.push(`${item.product} | ${item.price}`);
};
return choicesArray;
});
} // (node:3877) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: You must provide a `choices` parameter
You can't return a value from an async function like that. The function returns before your async values are ready. You either need to use a callback like:
function choices(cb) {
let choicesArray = [];
connection.query(`SELECT * FROM products`, (err, res)=>{
if (err) {
cb(err)
return
}
for (item of res) {
choicesArray.push(`${item.product} | ${item.price}`);
};
});
// wait here for query to complete
cb(null, choicesArray);
}
choices((err, value) =>{
if (err) {
// handle error
}
// use value here
})
Or return a promise like:
function choices() {
return new Promise((resolve, reject) => {
connection.query(`SELECT * FROM products`, (err, res)=>{
if (err) return reject(err)
let choicesArray = [];
for (item of res) {
choicesArray.push(`${item.product} | ${item.price}`);
}
resolve(choicesArray)
});
})
}
choices()
.then(value => {
// use value here
})
.catch(err =>{
// handle error
})