I doing an Async call using the below code, I want to know the value of data that is generated inside the getData() function however, I am getting undefined because the call is not resolved yet. Is there any way to sort it out?
getData(address){
let url = 'https://maps.googleapis.com/maps/api/geocode/json?address='+address+'&key='+this.key;
let call = this.http.get(url).subscribe(data =>{
let data = data.json()
return data;
});
}
//this is undefined because it does not know if the call was finished or not
console.log(this.latlong)
secondMethod(item){
//this is also undefined
this.getData(item.address)
}
Well, here is what you can do to solve it, simply push the data inside an array, then you may retrieve it in other functions.
getData(address){
let array = [];
let url = 'https://maps.googleapis.com/maps/api/geocode/json? address='+address+'&key='+this.key;
let call = this.http.get(url).subscribe(data =>{
let data = data.json()
array.push(data);
});
return array;
}
secondMethod(item){
//now you'll be able to retrieve it !
this.getData(item.address)
}
getData(address){
let url = 'https://maps.googleapis.com/maps/api/geocode/json?address='+address+'&key='+this.key;
return this.http.get(url).map(data => data.json());
})
Then you need to subscribe to get a value.
secondMethod(item){
this.getData(item.address).subscribe(data => {
console.log(data);
});
}
Here's how you could make that work keeping in mind async.
getData(address, callback){
let url = 'https://maps.googleapis.com/maps/api/geocode/json?
address='+address+'&key='+this.key;
let call = this.http.get(url).subscribe(callback);
}
secondMethod(item){
this.getData(item.address, this.thirdMethod.bind(this))
}
thirdMethod(data) {
let data = data.json()
// do stuff
this.something = data['something'];
}
Related
first off I'd like to make clear that I'm new to node.js and this may sound like a silly question but, how am I supposed to return data out of model.find() function in mongoose ( eg. with a var.exports = var )?<
const data = () =>
{
MyModel.find().then(function(result){
console.log(result);
return(result);
});
}
exports.data = data
Being the query asyncronous I'm not able to retrieve these data until the function is completed (so never). Is there anyway to return these informations in a variable eg:
const retriever = require('../utils/test.js') //calling the exports file
test = retriever.data
console.log(test)
Thank you very much in advance
With promises you can achieve it as follows
const data = () => {
return MyModel.find({});
}
// using it in another function
const result = await data();
1.You can use a callback, as below
const data = (callback) =>
{
MyModel.find().then(function(result){
console.log(result);
//return(result);
callback(results);//we pass in a function which will be used to pull out
//data
});
}
exports.data = data
// THE AREA WHERE THIS CODE IS USED
const {data} = require('./to/data')
//show data
data(function (result) {
console.log( result );// data from callback
})
2. using async/await promies
const data = async () =>
{
let results = await MyModel.find();
}
exports.data = data
ON USING THIS FUNCTION
const {data} = require('./to/data')
(async function () {
let res = await data();
console.log(res);
})()
I'm fetching nested object data. I can get that object data on console but when I try to get that data like return res.json(imageObject) I only get first key value pair of that object. This is the error to the console. UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
Console data is like this { bigImage: 'https://url.com' }
router.get("/", async(req, res) => {
//...fetch data
if (mediaData.type === "type1") {
let allData = await mediaData.media;
allData.map(async(Data) => {
if (Data.imageType === "big") {
let bigImage = await Data.url;
let imageObject = {
bigImage
};
console.log(imageObject);
return res.json(imageObject);
}
});
}
});
You are using res inside a .map, which will trigger it once for every item in the array you're iterating.
You can't do that. You can only use res once for each req, because you're replying to the browser. If you use it more than once, you get this error, which means it's already been used and you have already replied.
Solution : use res once, not inside a map.
Also, .map is useless here, because you're not interested in the result it produces. You should use forEach or even better, a for loop (which is breakable on condition).
You should use for..of.
It will stop the execution of your function at the return statement
router.get("/", async(req, res) => {
//...fetch data
if (mediaData.type === "type1") {
let allData = await mediaData.media;
let pendingResult = allData.filter(data => data.imageType === "big").map(async item => {
let bigImage = await item.url;
return { bigImage }
});
let result = await Promise.all(pendingResult);
res.json(result);
}
});
I got some data which I'm calling from API and I am using axios for that. When data is retrieved, I dump it inside of a function called "RefractorData()" just to organize it a bit, then I push it onto existing array. The problems is, my array gets populated inside forEach and I can console.log my data there, but once I exit the loop, my array is empty.
let matches: any = new Array();
const player = new Player();
data.forEach(
async (match: any) => {
try {
const result = await API.httpRequest(
`https://APILink.com/matches/${match.id}`,
false
);
if (!result) console.log("No match info");
const refractored = player.RefractorMatch(result.data);
matches.push({ match: refractored });
console.log(matches);
} catch (err) {
throw err;
}
}
);
console.log(matches);
Now the first console.log inside forEach is displaying data properly, second one after forEach shows empty array.
Managed to do it with Promise.all() and Array.prototype.map()
.
const player = new Player();
const matches = result.data;
const promises = matches.map(async (match: any) => {
const response: any = await API.httpRequest(
`https://API/matches/${match.id}`,
false
);
let data = response.data;
return {
data: player.RefractorMatch(data)
};
});
const response: any = await Promise.all(promises);
You must understand that async functions almost always run later, because they deppend on some external input like a http response, so, the second console.log is running before the first.
There a few ways to solve this. The ugliest but easiest to figure out is to create a external promise that you will resolve once all http requests are done.
let matches = [];
let promise = new Promise((resolve) => {
let complete = 0;
data.forEach((match: any) => {
API.httpRequest(...).then((result) => {
// Your logic here
matches.push(yourLogicResult);
complete++;
if (complete === data.length) {
resolve();
}
}
}
};
console.log(matches); // still logs empty array
promise.then(() => console.log(matches)); // now logs the right array
You can solve this using other methods, for example Promise.all().
One very helpful way to solve it is using RxJs Observables. See https://www.learnrxjs.io/
Hope I helped you!
I am trying to scrap wikipedia page to fetch list of airlines by first scrapping first page and then going to each individual page of airline to get the website url. I have divided the code in two functions. One to scrap main page and get a new url, and second function to scrap another page from the created url to get the website name from that page. I have used request-promise module for getting the html and then cheerio to parse the data.
export async function getAirlinesWebsites(req,res) {
let response = await request(options_mainpage);
console.log(`Data`);
let $ = cheerio.load(response);
console.log('Response got');
$('tr').each((i,e)=>{
let children = '';
console.log('inside function ', i);
if($(e).children('td').children('a').attr('class') !== 'new') {
children = $(e).children('td').children('a').attr('href');
let wiki_url = 'https://en.wikipedia.org' + children;
console.log(`wiki_url = ${wiki_url}`);
let airline_url = getAirlineUrl(wiki_url);
console.log(`airline_url = ${airline_url}`);
}
})
And then the getAirlineUrl() function will parse another page based on the provided url.
async function getAirlineUrl(url){
const wiki_child_options = {
url : url,
headers : headers
}
let child_response = await request(wiki_child_options);
let $ = cheerio.load(child_response);
let answer = $('.infobox.vcard').children('tbody').children('tr').children('td').children('span.url').text();
return answer;
})
However when I console log the answer variable in the parent function, I get a [object Promise] value instead of a String. How do I resolve this issue?
Async function return promise.In case of that,you need to use then to get resolved response or use await.
This should work if other part of your code is ok.
export async function getAirlinesWebsites(req, res) {
let response = await request(options_mainpage);
console.log(`Data`);
let $ = cheerio.load(response);
console.log("Response got");
$("tr").each(async (i, e) => {
let children = "";
console.log("inside function ", i);
if ($(e).children("td").children("a").attr("class") !== "new") {
children = $(e).children("td").children("a").attr("href");
let wiki_url = "https://en.wikipedia.org" + children;
console.log(`wiki_url = ${wiki_url}`);
let airline_url = await getAirlineUrl(wiki_url);
console.log(`airline_url = ${airline_url}`);
}
});
}
Since your getAirlineUrl function returns a promise, you need to await that promise. You can't have await nested inside of the .each callback because the callback is not an async function, and if it was it wouldn't work still. The best fix is the avoid using .each and just use a loop.
export async function getAirlinesWebsites(req,res) {
let response = await request(options_mainpage);
console.log(`Data`);
let $ = cheerio.load(response);
console.log('Response got');
for (const [i, e] of Array.from($('tr')).entries()) {
let children = '';
console.log('inside function ', i);
if($(e).children('td').children('a').attr('class') !== 'new') {
children = $(e).children('td').children('a').attr('href');
let wiki_url = 'https://en.wikipedia.org' + children;
console.log(`wiki_url = ${wiki_url}`);
let airline_url = await getAirlineUrl(wiki_url);
console.log(`airline_url = ${airline_url}`);
}
}
}
I´m trying to get a value from a function. This is what I´m trying to do:
In file favorite.ts
getUserFavsCount(userId: string){
let count = 0;
let favs = this.af.list(`/favorites/${userId}`).subscribe(data =>{
count = data.length;
});
console.log(count) // getting the value. here it shows correct
return count;
}
Now, in my profile.ts I´m trying to get this value:
countUserFav() {
this.userProvider.currentUser.first().subscribe((currentUser: User) => {
console.log("---->",this.favoriteProvider.getUserFavsCount(currentUser.$key)); // here I get 0 value always :(
this.myFavs = this.favoriteProvider.getUserFavsCount(currentUser.$key)
});
}
What Am I doing wrong?
As TheFallen has commented you'll need to return a promise. Always use promises or you'll have concurrence problems. Try this
getUserFavsCount = (userId: string): Promise<number> => {
// SINCE YOU RETURN A LENGTH, THEN THE PROMISE SHOULD RETURN A NUMBER
// THERE'S NO NEED TO DECLARE A COUNT VARIABLE SINCE YOU'LL NOT USE IT
return new Promise<number>(resolve =>{
let favs = this.af.list(`/favorites/${userId}`).subscribe(data =>{
resolve(data.length);
});
});
}
And then get your data
countUserFav() {
this.userProvider.currentUser.first().subscribe((currentUser: User) => {
this.favoriteProvider.getUserFavsCount(currentUser.$key).then(res =>{
this.myFavs = res;
});
});
}
Hope this helps :D