So I have this class called Events and I'm trying to call an async function from my server. I'm trying to understand where I went wrong / how to resolve a promise inside another async call. Any hints or tips would be greatly appreciated.
This is the event function I'm calling:
Event.prototype.getCost = function(name_of_place){
return new Promise( function(){
var placeID;
var GooglePlaces = require("node-googleplaces");
const places = new GooglePlaces(API_KEY);
const params = {
location: '40.689247,-123.102192',
radius: 1000
};
var query =
{
query: name_of_place
};
// ASYNC call
places.textSearch(query).then((res) => {
console.log("FIRST ASYNC CALL");
console.log(res.body.results[0].place_id);
placeID = res.body.results[0].place_id;
var request_place_details={
placeid : placeID
};
console.log(request_place_details);
return request_place_details;
}).then((request_place_details) => {
console.log("SECOND ASYNC CALL");
places.details(request_place_details).then((res) => {
console.log(res.body.result.price_level + " S");
var cost = res.body.result.price_level;
//trying to resolve getCost promise
//resolve(this.cost);
return cost;
}).then((cost) => {
console.log("ATTEMPT TO RESOLVE ORIGINAL");
this.cost = cost;
console.log(cost + " F");
//WANT TO RETURN THIS VALUE IN THE END
resolve(this.cost);
});
});
})};
This is where I'm calling it from:
//Server is currently serving on port 8420
app.listen(8420,function startServer(){
console.log("Listening on :: " + 8420);
var Event1 = new events(7,10,'New York');
// console.log(Event1.getCost('Sushi'));
Event1.getCost('Sushi').then(res => {
console.log(res);
})
});
your whole structure is way to complicated. when stripping the comments, you can reduce the code to this:
Event.prototype.getCost = function(name_of_place){
var GooglePlaces = require("node-googleplaces");
const places = new GooglePlaces(API_KEY);
//const params = {
// location: '40.689247,-123.102192',
// radius: 1000
//};
return places.textSearch({ query: name_of_place })
.then(res => res.body.results[0].place_id)
.then(placeID => places.details({ placeid : placeID }))
.then(res => res.body.result.price_level)
}
You're not declaring a promise correctly, return new Promise( function(resolve, reject){}
And then you can use that like so
places.details(request_place_details).then((res) => {
console.log(res.body.result.price_level + " S");
var cost = res.body.result.price_level;
// Just resolve it here.
resolve(cost);
});
// And here, res == cost.
Event1.getCost('Thai Moon').then(res=> {
console.log(res);
})
Also for reference, check out the Promise docs.
Related
I'm trying to create an app with routing capabilities and for this, I need to use the OpenRouteService-JS API. The way I have this set up is, what I assume to be, in a promise.
I can't get the array out of the promise, I've tried using an async function with await but that seems to be returning nothing and I don't know if the promise is set up wrong or the async function is set up wrong.
Here is the promise :
function getRoute() {
var openrouteservice = require("openrouteservice-js");
var Directions = new openrouteservice.Directions({ api_key: "######"});
// Direction Request
Directions.calculate({
coordinates: [[8.690958, 49.404662], [8.687868, 49.390139]],
profile: 'foot-walking',
extra_info: ['waytype', 'steepness'],
format: 'geojson'
})
.then(function(geojson) {
// Add your own result handling here
var PolyLine = geojson.features[0].geometry.coordinates.map(c => ({latitude: c[0], longitude: c[1]}))
console.log(PolyLine);
return PolyLine
})
.catch(function(err) {
var str = "An error occurred: " + err;
console.log(str);
});
}
Here is how I'm trying to collect it:
React.useEffect(() => {
async function getPolyLine() {
const res = await getRoute(); // type: Promise<Interface>
console.log(res)
setPolyLine(res);
}
getPolyLine();
}, [])
const [polyline, setPolyLine] = React.useState()
I have no real experience with Javascript or React and promises have been an interesting problem to stumble upon.
You need to add a return statement like this:
function getRoute() {
var openrouteservice = require("openrouteservice-js");
var Directions = new openrouteservice.Directions({ api_key: "######"});
// Direction Request
return Directions.calculate({
coordinates: [[8.690958, 49.404662], [8.687868, 49.390139]],
profile: 'foot-walking',
extra_info: ['waytype', 'steepness'],
format: 'geojson'
})
.then(function(geojson) {
// Add your own result handling here
var PolyLine = geojson.features[0].geometry.coordinates.map(c => ({latitude: c[0], longitude: c[1]}))
console.log(PolyLine);
return PolyLine
})
.catch(function(err) {
var str = "An error occurred: " + err;
console.log(str);
});
}
So in your code you aren't actually returning the promise.
I think here we have to create our custom Promise and resolve it with possible values to return a value to our desired statement.
As herein JavaScript, we cannot return statements directly from inner functions as you tried in the then clause.
Have a try with the following changes, Hope it will solve your problem:
function getRoute() {
var openrouteservice = require("openrouteservice-js");
var Directions = new openrouteservice.Directions({ api_key: "######"});
return new Promise((resolve, reject) => {
// Direction Request
Directions.calculate({
coordinates: [[8.690958, 49.404662], [8.687868, 49.390139]],
profile: 'foot-walking',
extra_info: ['waytype', 'steepness'],
format: 'geojson'
})
.then(function(geojson) {
// Add your own result handling here
var PolyLine = geojson.features[0].geometry.coordinates.map(c => ({latitude: c[0], longitude: c[1]}))
console.log(PolyLine);
resolve(PolyLine)
})
.catch(function(err) {
var str = "An error occurred: " + err;
console.log(str);
resolve([])
});
})
}
In my MochaJS Test, my Firebase data is not sending when I have Promise-terminating code in, but it will push when the code is not present (but the test won't terminate). How do I fix that?
Note: The output is the same, except it will hang if the Promise-resolving code is not present.
Output:
OK Attempting to inject new event with id eidMAEfy
Attempting to set /db/events/eidMAEfy to {
... JSON DATA ...
}
* HANGS HERE IF RESOLVE NOT PRESENT*
Test.js:
describe('*NEW* Create New Event Test Suite', async function() {
this.timeout(0);
let new_event_req_body = unformalized_new_test_event();
let is_preexisting = false;
it('creates a new event', async function() {
return new Promise(async function (resolve, reject) {
try {
let res = await utils.create_new_event(new_event_req_body, is_preexisting); // ISSUE. Remove line & it posts the data (but never terminates test)
assert.isOk('Ok','Ok');
resolve('ok');
} catch (e) {
assert.fail(e);
reject('noo');
}
})
});
});
Utils.js
var create_new_event = async (req_body, is_preexisting) => {
return new Promise( async function(resolve, reject) {
// make json
let new_event_json = new_event_result(req_body);
if (!new_event_json) reject("Invalid Input");
let event_id = new_event_json.id;
ok_log("Attempting to inject new event with id " + event_id);
let dbstring = '/db/events/'+event_id;
log('Attempting to set ' + dbstring + ' to ' + prettify(new_event_json));
// post data
let re = root.ref(dbstring);
re.set(new_event_json);
resolve(event_id); // Always resolves
});
}
I am reverse geo-coding a bunch of coordinates using a for loop and storing the result of each iteration in an array on node.js. However, when I send the array to my angular front-end or log it in the console, the order of the cities is randomized. I am using the #google/maps wrapper for node.js. Here is my code:
var gm = require('#google/maps').createClient({
key: '**********************************'
});
var x, z;
var places = [];
gm.directions({
origin: 'NYC',
destination: 'Washington DC',
}, function(err, response) {
if (!err) {
for(i=0; i<response.json.routes[0].legs[0].steps.length; i=i+3) {
x = response.json.routes[0].legs[0].steps[i].end_location; //getting the coordinates of the end location for each step
gm.reverseGeocode({latlng: [x.lat, x.lng],}, function(err, response){
if (!err) {
z = response.json.results[4].address_components[1].long_name + ', ' + response.json.results[4].address_components[2].short_name;
places.push(z);
}
});
}
}
});
setTimeout(() => {
console.log(places);
}, 5000)
I would really appreciate it if you could tell me what I am doing incorrectly here. Thanks.
UPDATE:
result of console.log(response.json.results[4])
result of console.log(g)
First things first, when you are working with async code, you shouldn't rely on setTimeout to getting a value. Sometimes the timeout would not be enough and you will get wrong data.
You can use callbacks, async/await and Promises solutions to handle async operations and guarantee the correctness of data.
Getting your example:
let gm = require("#google/maps").createClient({
key: "**********************************",
Promise: Promise
});
let x, z;
let places = [];
const directionsPromise = gm
.directions({
origin: "NYC",
destination: "Washington DC"
})
.asPromise()
.then(response => {
let geocodePromises = [];
for (i = 0; i < response.json.routes[0].legs[0].steps.length; i = i + 3) {
x = response.json.routes[0].legs[0].steps[i].end_location;
gecodePromises.push(
gm.reverseGeocode({ latlng: [x.lat, x.lng] }).asPromise()
);
}
return Promise.all(geocodePromises);
})
.then(geocodes => {
return geocodes.map(g => {
return (
g.json.results[4].address_components[1].long_name +
", " +
g.json.results[4].address_components[2].short_name
);
});
})
.catch(err => console.error(err));
directionsPromise.then(geocodes => {
// if you need
places = geocodes;
console.log(geocodes);
})
I hope this will help.
log after for loop, and check the iteration (i=i+3)
I'm trying to improve a firestore get function, I have something like:
return admin.firestore().collection("submissions").get().then(
async (x) => {
var toRet: any = [];
for (var i = 0; i < 10; i++) {
try {
var hasMedia = x.docs[i].data()['mediaRef'];
if (hasMedia != null) {
var docData = (await x.docs[i].data()) as MediaSubmission;
let submission: MediaSubmission = new MediaSubmission();
submission.author = x.docs[i].data()['author'];
submission.description = x.docs[i].data()['description'];
var mediaRef = await admin.firestore().doc(docData.mediaRef).get();
submission.media = mediaRef.data() as MediaData;
toRet.push(submission);
}
}
catch (e) {
console.log("ERROR GETTIGN MEDIA: " + e);
}
}
return res.status(200).send(toRet);
});
The first get is fine but the performance is worst on the line:
var mediaRef = await admin.firestore().doc(docData.mediaRef).get();
I think this is because the call is not batched.
Would it be possible to do a batch get on an array of mediaRefs to improve performance?
Essentially I have a collection of documents which have foreign references stored by a string pointing to the path in a separate collection and getting those references has been proven to be slow.
What about this? I did some refactoring to use more await/async code, hopefully my comments are helpful.
The main idea is to use Promise.all and await all the mediaRefs retrieval
async function test(req, res) {
// get all docs
const { docs } = await admin
.firestore()
.collection('submissions')
.get();
// get data property only of docs with mediaRef
const datas = await Promise.all(
docs.map(doc => doc.data()).filter(data => data.mediaRef),
);
// get all media in one batch - this is the important change
const mediaRefs = await Promise.all(
datas.map(({ mediaRef }) =>
admin
.firestore()
.doc(mediaRef)
.get(),
),
);
// create return object
const toRet = datas.map((data: MediaSubmission, i) => {
const submission = new MediaSubmission();
submission.author = data.author;
submission.description = data.description;
submission.media = mediaRefs[i].data() as MediaData;
return submission;
});
return res.status(200).send(toRet);
}
Im retrieving some data from firebase but the code won't wait for the data, it just keep running and some time later it retrieves the data. Im trying with await async but still... Whats wrong? Thanks!
var interlocutor = this.getRandomInterlocutor();
FriendlyChat.prototype.getRandomInterlocutor = async function(){
var numberOfUsers = await this.getNumberOfUsersRef();
console.log('numberOfUsers = ' + numberOfUsers);
var randomIndex = Math.floor(Math.random() * numberOfUsers);
console.log('randomIndex = ' + randomIndex);
var ref = await firebase.database().ref('companies/' + this.company + '/users/');
ref.limitToFirst(randomIndex).limitToLast(1).once('value').then(snapshot =>
{
var user = snapshot.val();
console.log('getRandomInterlocutor = ' + user);
});
}
FriendlyChat.prototype.getNumberOfUsersRef = async function(){
var numberOfUsersRef = await firebase.database().ref('companies/' + this.company + '/countregs');
var numberOfUsers;
numberOfUsersRef.on('value', function(snapshot) {
console.log(snapshot.val());
numberOfUsers = snapshot.val();
return numberOfUsers;
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
}
There are several things wrong here:
You are await-ing on the ref call, which is not async.
You are using on('value') which emits a stream of values, not a single read.
You are using callback-style code when async/await should be using Promises.
Here's a fixed version of your second function to demonstrate better practices:
FriendlyChat.prototype.getNumberOfUsersRef = async function(){
var numberOfUsersRef = firebase.database()
.ref('companies')
.child(this.company)
.child('countregs');
try {
var snapshot = await numberOfUsersRef.once('value');
return snapshot.val();
} catch (errorObject) {
console.log('The read failed:', errorObject.stack);
}
}