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);
}
}
Related
I'm new to learning Node.js, so I'm still getting used to asynchronous programming and callbacks. I'm trying to insert a record into a MS SQL Server database and return the new row's ID to my view.
The mssql query is working correctly when printed to console.log. My problem is not knowing how to properly return the data.
Here is my mssql query - in addJob.js:
var config = require('../../db/config');
async function addJob(title) {
var sql = require('mssql');
const pool = new sql.ConnectionPool(config);
var conn = pool;
let sqlResult = '';
let jobID = '';
conn.connect().then(function () {
var req = new sql.Request(conn);
req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
jobID = result['recordset'][0]['JobID'];
conn.close();
//This prints the correct value
console.log('jobID: ' + jobID);
}).catch(function (err) {
console.log('Unable to add job: ' + err);
conn.close();
});
}).catch(function (err) {
console.log('Unable to connect to SQL: ' + err);
});
// This prints a blank
console.log('jobID second test: ' + jobID)
return jobID;
}
module.exports = addJob;
This is my front end where a modal box is taking in a string and passing it to the above query. I want it to then receive the query's returned value and redirect to another page.
// ADD NEW JOB
$("#navButton_new").on(ace.click_event, function() {
bootbox.prompt("New Job Title", function(result) {
if (result != null) {
var job = {};
job.title = result;
$.ajax({
type: 'POST',
data: JSON.stringify(job),
contentType: 'application/json',
url: 'jds/addJob',
success: function(data) {
// this just prints that data is an object. Is that because I'm returning a promise? How would I unpack that here?
console.log('in success:' + data);
// I want to use the returned value here for a page redirect
//window.location.href = "jds/edit/?jobID=" + data;
return false;
},
error: function(err){
console.log('Unable to add job: ' + err);
}
});
} else {
}
});
});
And finally here is the express router code calling the function:
const express = require('express');
//....
const app = express();
//....
app.post('/jds/addJob', function(req, res){
let dataJSON = JSON.stringify(req.body)
let parsedData = JSON.parse(dataJSON);
const addJob = require("../models/jds/addJob");
let statusResult = addJob(parsedData.title);
statusResult.then(result => {
res.send(req.body);
});
});
I've been reading up on promises and trying to figure out what needs to change here, but I'm having no luck. Can anyone provide any tips?
You need to actually return a value from your function for things to work. Due to having nested Promises you need a couple returns here. One of the core features of promises is if you return a Promise it participates in the calling Promise chain.
So change the following lines
jobID = result['recordset'][0]['JobID'];
to
return result['recordset'][0]['JobID']
and
req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
to
return req.query(`INSERT INTO Jobs (Title, ActiveJD) VALUES ('${title}', 0) ; SELECT ##IDENTITY AS JobID`).then(function (result) {
and
conn.connect().then(function () {
to
return conn.connect().then(function () {
You may need to move code around that is now after the return. You would also be well served moving conn.close() into a single .finally on the end of the connect chain.
I recommend writing a test that you can use to play around with things until you get it right.
const jobId = await addJob(...)
console.log(jobId)
Alternatively rewrite the code to use await instead of .then() calls.
Im new with IndexedDB and I can not manipulate the data obtained from indexedDB table.I only need do a search values when a button is pressed, then the event activated with the button starts to work and it has to return many results, which may take a few seconds to return values, so I need to use async / await in the callback function. I think the problem is synchronous because I make the callback function async and the function getData() with which I get the data has the word await with it, even so, I can not work with the data, because when I do the console.log(x) it returns the undefined value.
let db;
let request = window.indexedDB.open("Cities", 1);
request.onerror = function (event) {
console.log("error")
};
request.onsuccess = function (event) {
db = event.target.result;
document.getElementById('search').addEventListener('click', async function () {
let x = await getData();
console.log(x)
})
};
function getData() {
let transaction = db.transaction(["City"], "readwrite");
transaction.oncomplete = function (event) {
document.querySelector('body').innerHTML += '<li>Transaction completed.</li>';
};
transaction.onerror = function () {
document.querySelector('body').innerHTML += '<li>Transaction not opened due to error: ' + transaction.error + '</li>';
};
let objectStore = transaction.objectStore("City");
let objectStoreRequest = objectStore.getAll();
objectStoreRequest.onsuccess = function () {
document.querySelector('body').innerHTML += '<li>Request successful.</li>';
let myRecord;
return myRecord = objectStoreRequest.result;
};
Of course, the console.log(x) is only to check that the data obtained is correct, once that point would come the part of the search but that is another story.
I'm not sure if my problem is with async / await or because I do not get the IndexedDB data correctly. Any help?
EDIT: -- I think I have found a solution, even though I think it is not the best way to solve the problem. I have moved all the code of the function getData() within the function that invokes the event, once the data is obtained I work within the method .onsuccess of objectStoreRequest, thus I avoid having to use async / await, I also continue working on the transaction which has not yet been finalized. If someone knows a cleaner way to make it work or explain to me why the original post code does not work, I would be very grateful.
I attach the code with which I am currently working:
let db;
let request = window.indexedDB.open("Cities", 1);
request.onerror = function (event) {
console.log("error")
};
request.onsuccess = function (event) {
db = event.target.result;
document.getElementById('search').addEventListener('click',function () {
let transaction = db.transaction(["City"], "readwrite");
transaction.oncomplete = function () {
document.querySelector('body').innerHTML += '<li>Transaction completed.</li>';
};
transaction.onerror = function (event) {
document.querySelector('body').innerHTML += '<li>Transaction not opened due to error: ' + transaction.error + '</li>';
};
let objectStore = transaction.objectStore("City");
let objectStoreRequest = objectStore.getAll();
objectStoreRequest.onsuccess = function () {
document.querySelector('body').innerHTML += '<li>Request successful.</li>';
let myRecord;
myRecord = objectStoreRequest.result;
console.log(myRecord)
}
})
};
Anyway it seems like no one have another way for resolve this, so Im go to respond myself this post with this response.
This is the only way I have found to solve the problem, although it seems like a dirty code, I think it could be improved.
let db;
let request = window.indexedDB.open("Cities", 1);
request.onerror = function (event) {
console.log("error")
};
request.onsuccess = function (event) {
db = event.target.result;
document.getElementById('search').addEventListener('click',function () {
let transaction = db.transaction(["City"], "readwrite");
transaction.oncomplete = function () {
document.querySelector('body').innerHTML += '<li>Transaction completed.</li>';
};
transaction.onerror = function (event) {
document.querySelector('body').innerHTML += '<li>Transaction not opened due to error: ' + transaction.error + '</li>';
};
let objectStore = transaction.objectStore("City");
let objectStoreRequest = objectStore.getAll();
objectStoreRequest.onsuccess = function () {
document.querySelector('body').innerHTML += '<li>Request successful.</li>';
let myRecord;
myRecord = objectStoreRequest.result;
console.log(myRecord)
}
})
};
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);
}
I want to return data to function that calls a function with firebase code, because of the asynchronous and nested structure of firebase queries it is not able to return values, I intend to use this logic to set tool tips in chart.js
Here is my code:
window.onload = function() {
get_data();
}
function get_data() {
var data = get_val();
console.log("...." + data);
}
function get_val() {
var label = "10/2/2017";
var Name = localStorage.getItem("VName");
console.log("Name:::" + Name);
var at_val;
var dbref = new Firebase("https://apraisalstaging.firebaseio.com/EmployeeDB/EInfo/");
dbref.once("value").then(function(snapshot) {
snapshot.forEach(function(childsnapshot) {
var data = childsnapshot.val();
var Nameval = data.Name;
if (Nameval == Name) {
console.log("Success");
Ikey = childsnapshot.key();
console.log("Key:::" + Ikey);
var dxRef = new Firebase("https://apraisalstaging.firebaseio.com/EmployeeDB/EApraise/" + Ikey);
dxRef.once("value").then(function(snapshot) {
snapshot.forEach(function(childsnapshot) {
var data = childsnapshot.val();
console.log(data);
if (label == data.Dateval) {
console.log("-------> bingo");
at_val = data.Attitude;
console.log("got value:" + at_val);
}
});
}).then(function() {
console.log("In then:" + at_val);
return at_val;
});
}
})
})
}
Data is loaded from the Firebase Database asynchronously. You cannot return a value now that hasn't been loaded yet. And until the async and await keywords are commonplace, you cannot make JavaScript wait for the async value.
The closest you can get today is to return a promise from get_val(). Then your calling code will be:
get_val().then(function(data) {
console.log("...." + data);
});
To do this you'll have to implement get_val() as:
function get_val() {
var label = "10/2/2017";
var Name = localStorage.getItem("VName") || "delta";
console.log("Name:::" + Name);
var at_val;
var dbref = firebase.database().ref("EmployeeDB/EInfo/").orderByChild("Name").equalTo(Name);
return dbref.once("value").then(function(snapshot) {
var promises = [];
snapshot.forEach(function(childsnapshot) {
var data = childsnapshot.val();
var Nameval = data.Name;
Ikey = childsnapshot.key;
var dxRef = firebase.database().ref("EmployeeDB/EApraise/" + Ikey).orderByChild("Dateval").equalTo(label);
promises.push(
dxRef.once("value").then(function(snapshot) {
snapshot.forEach(function(childsnapshot) {
var data = childsnapshot.val();
at_val = data.Attitude;
});
}).then(function() {
console.log("In then:" + at_val);
return at_val;
})
);
})
return Promise.all(promises);
})
}
I made a few changes to get_val():
it uses the new 3.x versions of the Firebase SDK. The solution could also work with 2.x, I just didn't feel like setting up a jsbin for that.
it populates a list of promises, one for each of the EApraise records
it returns a promise that resolves when all the EApraise records are loaded.
it uses Firebase queries to find the correct record. This removes the need for checking the values in the then() callbacks. But more importantly it ensures that only the necessary data is downloaded.
To make that last point true, you need to add a few index definitions to the rules in your Firebase Database console:
{
"rules": {
"EmployeeDB": {
"EInfo": {
".indexOn": "Name"
}
"EApraise": {
"$eid": {
".indexOn": "Dateval"
}
}
}
}
}
Here a jsbin with the working code: http://jsbin.com/jawamilawo/edit?js,console
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.