Firebase field delayed update - javascript

I have a Firebase with a users reference, which has a field user_id.
([Firebase]/[app]/users/user_id)
I insert a user & assign a user_id. However, when I read the value of user_id from userRef (a reference to users), it does not read it the first time. Subsequent reads work perfectly fine.
Using the debugger, I can see that when the user is created, a user_id is assigned. It seems like my first call refreshes the Firebase reference, so that subsequent calls are now seeing the udpated Firebase (I don't think that is what it is - that is just how it appears).
This is my code to read the value of user_id:
var userID = 0;
userRef.on('value', function(snapshot) {
userID = snapshot.val().user_id;
});
alert(userID);
The first time, the alert shows 0. Every time after that, it shows the correct value.
To make the problem even stranger, I also have a games reference with a game_id, created in the same way as user. But my read on game_id (in the exact same way & at the same time) works every time.
Any ideas?

The issue here is that .on() doesn't (in general) trigger your callback function immediately. In particular, the first time you do a .on() at a location, Firebase has to go ask the server for the current value, wait for the response, and THEN call your callback. And it does this all asynchronously.
The way your code is currently written, "alert(userID);" is being run before your callback code ("userID = snapshot.val().user_id;") so it always reports 0 the first time. The simple fix is to move the alert() inside your callback:
var userID = 0;
userRef.on('value', function(snapshot) {
userID = snapshot.val().user_id;
alert(userID);
});

Here's a common methodology to wait on two callbacks, using using jQuery's Promise model and once:
var userID = 0;
// sends a callback to fx where the results can be stored
function defer( fx ) {
return function() {
var deferred = $.Deferred();
fx( function(snapshot) { deferred.resolve(snapshot.val(); } );
return deferred.promise();
}
}
$.when( // wait for both actions to complete
defer( function(callback) { userRef.once('value', callback) }),
defer( function(callback) { widgetRef.once('value', callback) })
).then( function(values) {
// both deferreds are done now
// and values contains an array with the snapshot.val() from each call
console.log(values);
});

Related

Seeking proper way to use the get() method of INDEXEDDB data store

I am attempting to use a simple indexeddb 'get' method to retrieve a keyed record from an existing data store. For some reason that i have been unable to find, though the result of my 'get' request is 'successful', the returned value is 'undefined'.
The code below is called from an event listener that is fired when the user clicks on a button in a row of a table, indicating the user wants details on that particular row. Each row in the table represents a record in the indexeddb data store that has already been created. There are 8 values of interest in each row, but only 3 are displayed in my html table. I am simply attempting to access the row in the data store to get all 8 values in order to pass them along to the next process.
The following code is called from an event listener created on a 'Select' button for each row...
async function getGameInProgressDetails(gameID) {
try {
db = await idbConnect(DBName,DBVersion);
let tx = db.transaction(['gamesList'], 'readonly');
let gameStore = tx.objectStore('gamesList');
// I have confirmed the 'gameID' passed in is the key value that i would expect
// to retrieve the desired result.
let req = gameStore.get(gameID); // gameID comes from the selected row in the html table.
req.onsuccess = (ev) => {
console.log('Success');
let request = ev.target;
console.log(request); // returns an IDBRequest object with result: undefined and error: null.
let theGame = request.result;
console.log(theGame ); // displays 'undefined'.
}
req.onerror = (err) => {
console.warn(err);
};
} catch (e) {
console.warn(e);
}
I get the 'success' message indicating that the 'get' operation was successful, but my result is 'undefined'. Could someone please tell me if i am missing a step or if there's another way to go about this that i should look into? I've looked at a variety of tutorials and i must be overlooking something 'obvious'.
I discovered the problem with my code/approach and am answering my own question. The reason that the above code was not working was not because of my indexeddb code or logic at all.
What i discovered was that the index value that i had passed into the routine needed to be cast as an integer before the get() method call, JS was treating it as a string value. I was confused by the fact that i had checked the value in a console.log statement and it had shown the appropriate value. What i hadn't considered was how JS evaluated what type the variable value was.
For those coming later and seeing this, my final code was thus:
async function getGameInProgressDetails(gameID) {
db = await idbConnect(DBName,DBVersion);
let tx = db.transaction(['gamesList'], 'readonly');
var gameStore = tx.objectStore('gamesList');
let gameIndex = gameStore.index("gameIDIdx");
let request = gameIndex.get(parseInt(gameID)); //!! NOTE THE parseInt!!
request.onsuccess = function() {
if (request.result !== undefined) {
console.log("Games", request.result);
} else {
console.log("No such games.");
}
}

Get the current state in a transaction operation in Firebase

I have created a script that performs a request with firebase. Since I want to avoid multiple queries on a relation at the same time, I use the transaction function as seen in the code. First, I get the current status of the relation and check if it is present or not. This seems to work. The first time is const current = null and the function to add value to data2 will be executed.
And if I run the script the second time, then he goes into the next function successfully (doSecondFunction()).
And unfortunately, no currentData returns to me here. I expect the value of the ref data2 that was added the first time. But as a result, I get null.
Is there a way to work in the transaction with the current state of the database?
const ref = firebase.database().ref('data2');
ref.once('value', function(snapshot) {
const current = snapshot.val();
if(current == null){
console.log('FIRST');
addValToRefFunction();
}else{
console.log('SECOND');
doSecondFunction(current, ref);
}
});
function doSecondFunction(current, ref){
ref.transaction(function(currentData){
console.log(current); // result is the right value
console.log(currentData); // result is null ?
if (currentData === current){
console.log('Check');
return;
}
}
There is no way control what the client sees as the current status of a node when starting a transaction. In my experience the first time the callback gets invoked, the value is almost always null, and the callback needs to handle that.
Also see my answer here for a description of the process: Firebase runTransaction not working - MutableData is null

Calling function in Ionic controller after data loaded

I am pulling data from firebase and depending on the data, I adjust show a different image. My data is taking time to return and the conditional I wrote doesn't do anything because it runs before the data returns. How do I call the function after my data loads? I tried everything on the ionicView Docs. I also tried window.onload but that doesn't work. Thanks for your help in advance.
var firebaseRef = new Firebase("https://app.firebaseio.com/files/0/" + $stateParams.theId);
firebaseRef.once('value', function(dataSnapshot){
var dumData = dataSnapshot.val().data;
//this is just an integer
if (dumData > 3){
document.getElementById("pic").style.backgroundImage = 'url(img/pic2.png';
}
//Please ignore syntax errors as they do not exist in original code
Your issue is not related with ionic.
once returns a promise. So you should use the success callback to handle your data.
From Firebase once documentation:
// Provide a failureCallback to be notified when this
// callback is revoked due to security violations.
firebaseRef.once('value',
function (dataSnapshot) {
// code to handle new value
}, function (err) {
// code to handle read error
});

Right way or method Do While If Condition set inside loop?

I can't figure out the right way to do that:
I'm calling external API for products and I get a response I want to add those products to my database and if the response contains a next_page url loop it again until there is no next_page left
Here is what I came up with:
var products = api.ProductsActive('GET', {includes: 'Images', limit: 1});
requestProducts = function(){
products.results.forEach(function(product){
var sameproduct = apiProducts.findOne({listing_id: product.listing_id});
if (sameproduct) {
console.log('found sameproduct');
return;
}
//Add userId to current product so we can assosicate products belong to "X" user
var productExtend = _.extend(product, {userId: Meteor.userId()});
apiProducts.insert(productExtend);
});
//If there is next page get the next page number and get products
var nextPage = products.pagination.next_page;
if (nextPage !== null) {
products = api.ProductsActive('GET', {includes: 'Images', page: nextPage, limit: 1});
console.log(products);
}
};
//loop at least once, and then if more pages found
do {
requestProducts();
}
while (products.pagination.next_page !== null);
Which doesn't seems to work right I'm not sure if this is the right method for such function I really help your input! Please help me figure out the right way for this!
Keep in mind javascript is asynchronous, so you need to handle your loop with callbacks instead of do. The reason for this is requestProducts likely uses a callback and the javascript interpreter doesn't "wait" for requestProducts() to finish running before it runs the next line of code.
You haven't provided any details of how the api works so I have to generalise.
If requestProducts() takes a parameter as a callback (which it should), use that to re-run itself:
var callback = function(err, result) {
if(products.pagination.next_page !== null) requestProducts(callback);
};
requestProducts(callback);
You would have to check the your api's documentation to check how the callback is defined but it would be something like the above where err is returned if there is a problem.
How this works is, when requestProducts is called the callback fires and if its not the last page to repeat itself until ..next_page == null

Parse Cloud Code Ending Prematurely?

I'm writing a job that I want to run every hour in the background on Parse. My database has two tables. The first contains a list of Questions, while the second lists all of the user\question agreement pairs (QuestionAgreements). Originally my plan was just to have the client count the QuestionAgreements itself, but I'm finding that this results in a lot of requests that really could be done away with, so I want this background job to run the count, and then update a field directly on Question with it.
Here's my attempt:
Parse.Cloud.job("updateQuestionAgreementCounts", function(request, status) {
Parse.Cloud.useMasterKey();
var query = new Parse.Query("Question");
query.each(function(question) {
var agreementQuery = new Parse.Query("QuestionAgreement");
agreementQuery.equalTo("question", question);
agreementQuery.count({
success: function(count) {
question.set("agreementCount", count);
question.save(null, null);
}
});
}).then(function() {
status.success("Finished updating Question Agreement Counts.");
}, function(error) {
status.error("Failed to update Question Agreement Counts.")
});
});
The problem is, this only seems to be running on a few of the Questions, and then it stops, appearing in the Job Status section of the Parse Dashboard as "succeeded". I suspect the problem is that it's returning prematurely. Here are my questions:
1 - How can I keep this from returning prematurely? (Assuming this is, in fact, my problem.)
2 - What is the best way of debugging cloud code? Since this isn't client side, I don't have any way to set breakpoints or anything, do I?
status.success is called before the asynchronous success calls of count are finished. To prevent this, you can use promises here. Check the docs for Parse.Query.each.
Iterates over each result of a query, calling a callback for each one. If the callback returns a promise, the iteration will not continue until that promise has been fulfilled.
So, you can chain the count promise:
agreementQuery.count().then(function () {
question.set("agreementCount", count);
question.save(null, null);
});
You can also use parallel promises to make it more efficient.
There are no breakpoints in cloud code, that makes Parse really hard to use. Only way is logging your variables with console.log
I was able to utilize promises, as suggested by knshn, to make it so that my code would complete before running success.
Parse.Cloud.job("updateQuestionAgreementCounts", function(request, status) {
Parse.Cloud.useMasterKey();
var promises = []; // Set up a list that will hold the promises being waited on.
var query = new Parse.Query("Question");
query.each(function(question) {
var agreementQuery = new Parse.Query("QuestionAgreement");
agreementQuery.equalTo("question", question);
agreementQuery.equalTo("agreement", 1);
// Make sure that the count finishes running first!
promises.push(agreementQuery.count().then(function(count) {
question.set("agreementCount", count);
// Make sure that the object is actually saved first!
promises.push(question.save(null, null));
}));
}).then(function() {
// Before exiting, make sure all the promises have been fulfilled!
Parse.Promise.when(promises).then(function() {
status.success("Finished updating Question Agreement Counts.");
});
});
});

Categories

Resources