I'm gonna do my best to explain this as I'm new to using javascript along with Firebase SDK. Here is the mock data I have:
"Via" : {
"00zz00" : {
"coordinates" : "0,0",
"order_number" : 1,
"route_id" : "xyz987",
"via_id" : "00zz00"
},
"1a2b3c" : {
"coordinates" : "10,-10.1",
"order_number" : 1,
"route_id" : "abc123",
"via_id" : "1a2b3c"
},
"2b3c4d" : {
"coordinates" : "45.5,-24.7",
"order_number" : 2,
"route_id" : "abc123",
"via_id" : "2b3c4d"
},
"3c4d5e" : {
"coordinates" : "45.8,-24.0",
"order_number" : 4,
"route_id" : "abc123",
"via_id" : "3c4d5e"
},
"4d5e6f" : {
"coordinates" : "20.0,-20.0",
"order_number" : 3,
"route_id" : "abc123",
"via_id" : "4d5e6f"
}
}
I first made a query using .on() with the "child_added" event to fill an array with the coordinates of every result:
var waypoints = new Array();
var refVia = firebase.database().ref("Via");
refVia
.orderByChild("route_id")
.equalTo("abc123")
.on("child_added", function (snapshot) {
waypoints.push(snapshot.val().coordinates);
});
I am calling another API and using the array to create a route and I realized I was getting an error since my array was still empty. I found a solution on here mentioning using .once().then() with the "value" event along with a promise but I'm a bit confused about how to go from here. I tried this but I'm a bit lost from here.
var refVia = firebase.database().ref("Via");
return refVia
.orderByChild("route_id")
.equalTo("abc123")
.once("value")
.then(
function (snapshot) {
var via = [];
//Not sure what to do here to add the entries to my array...
return Promise.all(via);
},
function (error) {
console.error(error);
}
);
I understand that you need to iterate over the keys of the nodes (which are equal to the via_id subnode values) in order to define promises that you add to the via Array. So the following should do the trick:
var refVia = firebase.database().ref('Via');
return refVia
.orderByChild('route_id')
.equalTo('abc123')
.once('value')
.then((snapshot) => {
const via = [];
for (const [key, value] of Object.entries(snapshot.val())) {
console.log(`${key}: ${value.via_id}`);
via.push(*** here build the Promise based on the key value ***)
// example with Axios https://github.com/axios/axios
via.push(axios.get('https://..../via?ID=' + key));
}
return Promise.all(via);
})
.then(results => {
// results is an Array of results of each call to the API
// For example
results.forEach(r => {
// ...
})
})
.catch((error) => {
console.error(error);
});
Related
I would be glad if I can get some help with my first question. I am an Android developer and I am currently doing a project which requires Firebase cloud functions, which I am new to.
I am calling an external API with Axios, and saving the JSON response into multiple Firestore documents. 4 out of 5 times that I call the function, it executes without error, but it does not write to Firestore, and the 1 time it writes to Firestore, it takes as long as 8 to 12 seconds to write the data.
I read something about promises and I applied to the function(to the best of my knowledge) but the problem persists. Please let an expert tell me what I am doing wrong. Below is my cloud function:
const functions = require("firebase-functions");
const axios = require("axios");
const admin = require("firebase-admin");
const api_token = "---------";
const includes = "------------";
const url = "------------" + api_token + includes;
exports.liveScores = functions.https.onRequest((req, res) => {
return axios.get(url)
.then(response => {
var data = response.data.data;
data.forEach(function(obj){
admin.firestore().collection("fixtures").doc(obj.id.toString()).set({
id : obj.id,
league_id : obj.league_id,
venue : obj.venue_id,
localteam_id : obj.localteam_id,
visitorteam_id : obj.visitorteam_id,
round_id : obj.round_id,
round_name : obj.round ? obj.round.data.name : null,
stage_id : obj.stage_id,
localteam_score : obj.scores.localteam_score,
visitorteam_score : obj.scores.visitorteam_score,
status : obj.time.status,
timestamp : new admin.firestore.Timestamp(obj.time.starting_at.timestamp, 0),
real_date : obj.time.starting_at.date,
minute : obj.time.minute,
injury_time : obj.time.injury_time,
localteam_formation : obj.formations.localteam_formation,
visitorteam_formation : obj.formations.visitorteam_formation,
visitorTeam : obj.visitorTeam.data.name,
localTeam : obj.localTeam.data.name,
local_team_logo : obj.localTeam.data.logo_path,
visitor_team_logo : obj.visitorTeam.data.logo_path,
season_id : obj.season_id,
referee : obj.referee ? obj.referee.data.fullname : null,
aggregate : obj.aggregate ? obj.aggregate.data.result : null,
venue_name : obj.venue && obj.venue.data.name ? obj.venue.data.name : null,
weather_type : obj.weather_report && obj.weather_report.type ? obj.weather_report.type : null,
weather_icon : obj.weather_report && obj.weather_report.icon ? obj.weather_report.icon : null,
temperature : obj.weather_report && obj.weather_report.temperature_celcius ? obj.weather_report.temperature_celcius.temp : null
}).then(() => {
console.log("Fixtures table complete...");
})
var no_of_events = obj.events.data.length;
for(var i=0; i < no_of_events; i++ ){
admin.firestore().collection("events").doc(obj.events.data[i].fixture_id.toString()).collection("all_events").doc(obj.events.data[i].id.toString()).set({
type : obj.events.data[i].type,
team_id : obj.events.data[i].team_id,
fixture_id : obj.events.data[i].fixture_id,
var_result : obj.events.data[i].var_result,
player_id : obj.events.data[i].player_id,
player_name : obj.events.data[i].player_name,
related_player_id : obj.events.data[i].related_player_id,
related_player_name : obj.events.data[i].related_player_name,
minute : obj.events.data[i].minute,
reason : obj.events.data[i].reason,
id : obj.events.data[i].id
}).then(() => {
console.log("Events table complete...");
})
}
for(var y=0; y < obj.lineup.data.length; y++){
admin.firestore().collection("lineup").doc(obj.lineup.data[y].fixture_id.toString()).collection("lineup").doc(obj.lineup.data[y].player_id.toString()).set({
fixture_id : obj.lineup.data[y].fixture_id,
team_id : obj.lineup.data[y].team_id,
player_id : obj.lineup.data[y].player_id,
player_name : obj.lineup.data[y].player_name,
number : obj.lineup.data[y].number,
position : obj.lineup.data[y].position,
formation_position : obj.lineup.data[y].formation_position,
captain : obj.lineup.data[y].captain
}).then(() => {
console.log("Lineup table complete...");
})
}
for(var x=0; x < obj.bench.data.length; x++){
admin.firestore().collection("bench").doc(obj.bench.data[x].fixture_id.toString()).collection("bench").doc(obj.bench.data[x].player_id.toString()).set({
fixture_id : obj.bench.data[x].fixture_id,
team_id : obj.bench.data[x].team_id,
player_id : obj.bench.data[x].player_id,
player_name : obj.bench.data[x].player_name,
number : obj.bench.data[x].number,
position : obj.bench.data[x].position,
formation_position : obj.bench.data[x].formation_position,
captain : obj.bench.data[x].captain
}).then(() => {
console.log("Bench table complete...");
})
}
for(var c=0; c < obj.stats.data.length; c++){
admin.firestore().collection("stats").doc(obj.stats.data[c].fixture_id.toString()).collection("all_stats").doc(obj.stats.data[c].team_id.toString()).set({
fixture_id : obj.stats.data[c].fixture_id,
team_id : obj.stats.data[c].team_id,
total_shots : obj.stats.data[c].shots && obj.stats.data[c].shots.total ? obj.stats.data[c].shots.total : null,
ongoal : obj.stats.data[c].shots && obj.stats.data[c].shots.ongoal ? obj.stats.data[c].shots.ongoal : null,
total_passes : obj.stats.data[c].passes && obj.stats.data[c].passes.total ? obj.stats.data[c].passes.total : null,
attacks : obj.stats.data[c].attacks && obj.stats.data[c].attacks.attacks ? obj.stats.data[c].attacks.attacks : null,
fouls : obj.stats.data[c].fouls,
corners : obj.stats.data[c].corners,
offsides : obj.stats.data[c].offsides,
possessiontime : obj.stats.data[c].possessiontime,
yellowcards : obj.stats.data[c].yellowcards,
redcards : obj.stats.data[c].redcards,
injuries : obj.stats.data[c].injuries,
saves : obj.stats.data[c].saves,
tackles : obj.stats.data[c].tackles
}).then(() => {
console.log("Stats table complete...");
})
}
})
return res.status(200).json(
console.log("successful")
)
})
.catch(err => {
console.log(err);
return res.status(500).json({
error: err
})
})
});
I would appreciate any kind of help.
Thank you.
You should probably not launch all those firestore queries simultaneously, but wait for each one to complete before launching the next one.
This is easier when you make the onRequest callback function async:
exports.liveScores = functions.https.onRequest(async (req, res) => {
try {
let response = await axios.get(url);
var data = response.data.data;
for (let obj of data) {
await admin.firestore().collection("fixtures").doc(obj.id.toString()).set({
id: obj.id,
/* ... */
});
console.log("Fixtures table complete...");
for (let eventData of obj.events.data){
await admin.firestore().collection("events").doc(eventData.fixture_id.toString()).collection("all_events").doc(eventData.id.toString()).set({
type: eventData.type,
/* ... */
});
}
console.log("Events table complete...");
for (let lineupData of obj.lineup.data) {
await admin.firestore().collection("lineup").doc(lineupData.fixture_id.toString()).collection("lineup").doc(lineupData.player_id.toString()).set({
fixture_id: lineupData.fixture_id,
/* ... */
});
}
console.log("Lineup table complete...");
/* ... etc ... */
}
console.log("successful");
return res.status(200).json({ message: "successful" });
} catch(error) {
console.log(error);
return res.status(500).json({ error });
}
});
NB: don't pass console.log("successful") as an argument to .json. You'll want to pass an object as argument.
I'm trying to gain an understanding of query in Firebase. I want to pass userId A & B and find out if they are subscribed to a common chatId, it will either return true or false.
How can query both userId and evaluate results for my desired output?
export const checkForExistingChat = (currentUserId, recipient) => {
var IdList = {}
var query = database
.ref(`Chats/${currentUserId}`)
.orderByChild("subscribedToChat")
.once("value", function (dataSnapshot) {
dataSnapshot.forEach(function (childSnapshot) {
const childData = childSnapshot.val();
console.log("childData : ", childData);
});
});
};
Export JSON of Chat
"Chats" : {
"61vtPjp8YVVSzpvexwXMgEHghYf1" : {
"subscribedToChat" : {
"1a555cbf-30b7-4c8f-9986-4252a7620c45" : "1a555cbf-30b7-4c8f-9986-4252a7620c45",
"2d718ea7-eafa-48db-af14-f165f07b3b08" : "2d718ea7-eafa-48db-af14-f165f07b3b08",
"2e4fd8bb-4afb-4229-83ec-5a427fe2731d" : "2e4fd8bb-4afb-4229-83ec-5a427fe2731d",
"35c537ef-57dd-48c5-9350-5d1fe2e9d118" : "35c537ef-57dd-48c5-9350-5d1fe2e9d118",
"3a816ac1-6e97-4d66-ae19-77e65f8c2df4" : "3a816ac1-6e97-4d66-ae19-77e65f8c2df4",
}
},
"qqpBNbEa8ZSiCEUlseFeGeiRqzh2" : {
"subscribedToChat" : {
"1a555cbf-30b7-4c8f-9986-4252a7620c45" : "1a555cbf-30b7-4c8f-9986-4252a7620c45",
"35c537ef-57dd-48c5-9350-5d1fe2e9d118" : "35c537ef-57dd-48c5-9350-5d1fe2e9d118"
}
}
}
Since you already know how to load data from Firebase, this is essentially a non-Firebase problem: finding the overlapping keys in two lists of keys.
A quick code snippet:
var json = {
"Chats" : {
"61vtPjp8YVVSzpvexwXMgEHghYf1" : {
"subscribedToChat" : {
"1a555cbf-30b7-4c8f-9986-4252a7620c45" : "1a555cbf-30b7-4c8f-9986-4252a7620c45",
"2d718ea7-eafa-48db-af14-f165f07b3b08" : "2d718ea7-eafa-48db-af14-f165f07b3b08",
"2e4fd8bb-4afb-4229-83ec-5a427fe2731d" : "2e4fd8bb-4afb-4229-83ec-5a427fe2731d",
"35c537ef-57dd-48c5-9350-5d1fe2e9d118" : "35c537ef-57dd-48c5-9350-5d1fe2e9d118",
"3a816ac1-6e97-4d66-ae19-77e65f8c2df4" : "3a816ac1-6e97-4d66-ae19-77e65f8c2df4",
}
},
"qqpBNbEa8ZSiCEUlseFeGeiRqzh2" : {
"subscribedToChat" : {
"1a555cbf-30b7-4c8f-9986-4252a7620c45" : "1a555cbf-30b7-4c8f-9986-4252a7620c45",
"35c537ef-57dd-48c5-9350-5d1fe2e9d118" : "35c537ef-57dd-48c5-9350-5d1fe2e9d118"
}
}
}
};
var keys1 = Object.keys(json.Chats["61vtPjp8YVVSzpvexwXMgEHghYf1"].subscribedToChat);
var keys2 = Object.keys(json.Chats["qqpBNbEa8ZSiCEUlseFeGeiRqzh2"].subscribedToChat);
console.log(keys1, keys2);
var commonKeys = keys1.filter(function(key) {
return keys2.indexOf(key) >= 0;
});
console.log(commonKeys);
This is an O(n^2) algorithm, but I doubt that'll be a concern on the list sizes you're likely to have. If it is a concern, and the lists are sorted, you can keep a cursor in each least, and move forward only through them once to make it an O(2n) algorithm.
I'm writing cloud functions for my firebase realtime database (not firestore) using js sdk. These functions increment counters that count the number of users per area. The areas I'm using are geographical and nested (Continent>Country>Region>City). The functions are triggered by the insertion of a user into the users list of a given area (.onCreate). If a user is inserted into the users list of let's say a country, it will automatically be added to the users list of the parent continent.
After tinkering for some time I was only able to come up with a "low-level" non-scalable solution (which by the way works). This means that I need to write additionnal code if I want to add a child area to city for example.
This is a problem because I expect to add child areas in the future.
Question: Is there a way to write a generic cloud function that increments the counters of a node and the counters of all its parent nodes?
I'm quite new to firebase and any help would be really appreciated
Json structure of firebase realtime db
{
"groups" : {
"location" : {
"continents" : {
"EU" : {
"countries" : {
"DE" : {
"created" : "2019-03-25--20:03:34",
"flag" : "/flags/de.svg",
"label" : "Germany",
"level" : 2,
"regions" : {
"BE" : {
"cities" : {
"Berlin" : {
"created" : "2019-03-25--20:03:35",
"label" : "Berlin",
"level" : 3,
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:55",
"label" : "Anonymous"
}
}
}
},
"created" : "2019-03-25--20:03:35",
"label" : "Land Berlin",
"level" : 3,
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:52",
"label" : "Anonymous"
}
}
}
},
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:49",
"label" : "Anonymous"
}
}
}
},
"created" : "2019-03-25--20:03:33",
"label" : "Europe",
"level" : 1,
"type" : "location",
"usercount" : 1,
"users" : {
"rvDUa5n0oufRFGA9JB7lioom0ac2" : {
"created" : "2019-03-25--20:03:46",
"label" : "Anonymous"
}
}
}
}
}
}
}
My cloud functions for user insertion - still need to write for user deletion
exports.onUserAddToContinent = functions.database
.ref('/groups/location/continents/{continentID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
exports.onUserAddToCountry = functions.database
.ref('/groups/location/continents/{continentID}/countries/{countryID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const countryId = context.params.countryID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/countries/' + countryId + '/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
exports.onUserAddToRegion = functions.database
.ref('/groups/location/continents/{continentID}/countries/{countryID}/regions/{regionID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const countryId = context.params.countryID
const regionId = context.params.regionID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/countries/' + countryId + '/regions/' + regionId +'/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
exports.onUserAddToCity = functions.database
.ref('/groups/location/continents/{continentID}/countries/{countryID}/regions/{regionID}/cities/{cityID}/users/{userID}')
.onCreate((snapshot, context) => {
const continentId = context.params.continentID
const countryId = context.params.countryID
const regionId = context.params.regionID
const cityId = context.params.cityID
const counterRef = admin.database().ref('/groups/location/continents/' + continentId + '/countries/' + countryId + '/regions/' + regionId +'/cities/'+ cityId + '/usercount');
return counterRef.transaction(usercount => {
return (usercount || 0) + 1
})
})
I feel 'im missing something. There has to be a generic way of doing this.
I am trying to get my nodejs controller to update the rate in the currency table.
Everything works fine in S3T / RoboMongo, but for some reason it just wont fire the update inside the nodejs controller.
Here is my currency table
{
"_id" : "USD",
"index" : NumberInt(6),
"name" : "Dollar",
"currency" : "USD",
"symbol" : "$",
"active" : true,
"default" : false,
"rate" : 0
}
{
"_id" : "EUR",
"index" : NumberInt(2),
"name" : "Euro",
"currency" : "EUR",
"symbol" : "€",
"active" : true,
"default" : false,
"rate" : 0
}
I tried both of these, works fine in S3T but not inside nodejs:
db.currency.update (
{ _id : "EUR" },
{ $set: { rate : 123 }},
{ upsert: true }
)
db.currency.updateOne (
{ _id : "EUR" },
{ $set: { rate : 123 }},
{ upsert: true }
)
Here is my nodejs code:
var mongoose = require('mongoose');
var currencyModel = require('../models/currencyModel');
var currencyTable = mongoose.model('currencyModel');
var updateRates = () => {
return new Promise((resolve, reject) => {
for (var key in data.quotes) {
var currencyID = key.substring(3);
var newRate = (data.quotes[key] * THBUSD).toFixed(5);
console.log("currencyID: " + currencyID)
console.log("newRate: " + newRate)
currencyTable.update (
{ _id: currencyID },
{ $set: { rate : newRate }},
{ upsert: true }
),function (err, data) {
if (err) {
reject(new Error('updateRates: ' + err));
};
};
};
resolve();
})};
And here is my currencyModel (which is where I think the problem is?!?)
// Currency Model
// This model is the structure containing data from the Currency table
//
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var currencySchema = new Schema({
_id: String, // Unique Currency code
index: Number, // Indes for sorting
name: String, // Currency name
symbol: String, // Currency symbol
active: Boolean, // Active True False
rate: Number // Exchange rate (multiply with THB price)
});
module.exports = mongoose.model('currencyModel', currencySchema, 'currency');
I cannot see why it wont fire the currencyTable.update from inside nodejs.
I turned debug on in mongoose, and I see all other mongodb operations in the console like Mongoose: price.findOne({ _id: 'ATL-D406' }, { fields: {} }) etc.. but I do not see this currency.update in the console, which is why I dont think its fired off to mongodb - and I cannot see the reason.
You have a "loop" that completes execution before the inner callbacks fire. Instead just use Promises all the way through and call Promise.all() to collect all the iterated Promises and resolve them:
var updaterates = () => {
return Promise.all(
Object.keys(data.quotes).map(k => {
return currencyTable.update(
{ _id: k.substring(0,3) },
{ $set: { rate : (data.quotes[k] * THBUSD).toFixed(5) }},
{ upsert: true }
).exec()
});
)
};
The returned response of Promise.all() is an array of the response objects from the updates. Also note that this is a "fast fail" operation and calls will be made in parallel.
Object.keys() returns an "array of the key names" in the specified object. .map() iterates those keys and returns an "array" of the return value for the iterator.
We use the k as the "key name" to access the wanted key from data.quotes and use the values to perform each .update() with .exec() to return a "real" Promise. The iterator returns an "array" Promise which becomes the argument to Promise.all().
I am using googles api to get 3 fields,
name rating and place review. Since place review uses another api call I created it as a function to get the data inside my place_review.
response.results.forEach((entry)=>{
var restaurantName = {
"name" : entry.name,
"rating" : entry.rating,
"place_review" : placeReview(entry.place_id)
}
arr.push(restaurantName);
console.log(restaurantName);// shows only name and rating
});
The extra function
function placeReview(place_id){
console.log(place_id) // i see all place id via forloop.
googlePlaces.placeDetailsRequest({placeid: place_id},function(error,response){
if (error) throw error;
return response.result.reviews[0].text;
});
}
When I run the code , i get name : somename, rating : 4.5 and place_review : null
I thought the forloop will call the function and the return of the function will append the string to "place_review" not sure what I am doing wrong.
The placeDetailsRequest api is asynchronous, meaning it is initiated but doesn't complete right away. Consequently, the line:
return response.result.reviews[0].text;
is meaningless because there is nothing to return to.
Your console.log(restaurantName); will be run before the request completes so you will only get an 'undefined'.
To fix this, you will need to do something a fair bit more complicated ( will assume, for now that Promises are something you are not familiar with but that is typically the way people go once they are comfortable with the way asynchronous programming works):
response.results.forEach((entry)=>{
var restaurantName = {
"name" : entry.name,
"rating" : entry.rating,
"place_review" : undefined // will be filled in later
}
placeReview(entry.place_id, restaurantName);
});
function placeReview(place_id, obj){
console.log(place_id) // i see all place id via forloop.
googlePlaces.placeDetailsRequest({placeid: place_id},function(error,response){
if (error) throw error;
obj.place_review = response.result.reviews[0].text;
console.log(obj);
});
}
Of course, you will only see the console results as each request finishes.
This uses a technique called 'callbacks' to manage the information that is only available at a later time. There is lots of information about this online and on SO.
Here's a version that you can run to see the technique in action - very similar to above but hard-wired some data.
var list = [
{ name: 'Aaron', rating: '14', place_id: 21 },
{ name: 'Brad', rating: '33', place_id: 33 }
];
list.forEach(function (entry) {
var restaurantName = {
"name" : entry.name,
"rating" : entry.rating,
"place_review" : undefined // will be filled in later
};
placeReview(entry.place_id, restaurantName);
});
function placeDetailsRequest(idObj, cb) {
setTimeout(function() {
var result = (idObj.placeid === 21) ? 'def' : 'abc';
cb(null, { result: { reviews: [{ text: result}]}});
}, 2000);
}
function placeReview(place_id, obj) {
console.log(place_id); // i see all place id via for loop.
placeDetailsRequest( { placeid: place_id }, function(error,response){
if (error) throw error;
obj.place_review = response.result.reviews[0].text;
console.log(obj);
});
}
Further Edit to Show Promise Solution:
var list = [
{ name: 'Aaron', rating: '14', place_id: 21 },
{ name: 'Brad', rating: '33', place_id: 33 }
];
var allPromises = list.map(function (entry) {
var restaurantName = {
"name" : entry.name,
"rating" : entry.rating,
"place_review" : undefined // will be filled in later
};
return placeReview(entry.place_id, restaurantName);
});
Promise.all(allPromises)
.then(results => console.log(results))
.catch(error => console.log(error));
function placeReview(place_id, obj) {
return new Promise((resolve, reject) => {
// Here's where you would do your google api call to placeDetailsRequest
// if error then reject the promise, otherwise resolve it with your results
//googlePlaces.placeDetailsRequest({placeid: place_id},function(error,response){
// if (error) {
// reject(error);
// } else {
// obj.place_review = response.result.reviews[0].text;
// resolve(obj);
// }
//});
// simulating with delayed response
setTimeout(() => {
var result = (place_id === 21) ? 'def' : 'abc';
obj.place_review = result;
resolve(obj);
}, 2000);
});
}
There are other ways to approach this, but in this solution I am resolving the promise with the completed object. The Promise.all() function then either shows the successful results or the catch will display an error.
Promises
You could use Q in order to implement promises in your place review function:
function placeReview (place_id) {
var deferred = Q.defer();
googlePlaces.placeDetailsRequest({placeid: place_id},function(error,response){
if (error) deferred.reject(err);
else deferred.resolve(response.result.reviews[0].text);
});
return deferred.promise // the promise is returned
}
then you can invoke that function in your processing logic like this:
response.results.forEach((entry)=>{
placeReview(entity.place_id).then(function(text) {
var restaurantName = {
"name" : entry.name,
"rating" : entry.rating,
"place_review" : text
}
arr.push(restaurantName);
console.log(restaurantName);// shows only name and rating
});
});
you will not push the restaurant details till your call to the API is complete.
Async option
You could use async to coordinate your calls:
This function will be executed once per each entry:
function placeReview(entry, callback){
console.log(entry.place_id)
googlePlaces.placeDetailsRequest({entry.placeid: place_id},function(error,response){
if (error) throw error;
//this call back retrives the restaurant entity that async stack in a list
callback(null,{
"name" : entry.name,
"rating" : entry.rating,
"place_review" : response.result.reviews[0].text
});
});
}
Then you ask async to iterate your result array and execute placeReview for each one. Async will gather results in an array:
async.map(response.results, placeReview, function(err, results) {
// results will have the results of all 2
console.log(results); //this is your array of restaurants
});