Proper javascript functions structure - javascript

I'm getting data from my firebase
//user balance check
var balanceRef = new Firebase('https://****.firebaseIO.com/arcade/grabbit/'+category+'/'+brand+'/'+gameId+'/'+'activePlayers'+'/'+userId+'/');
Here's the function that gets the data
//check user balance
function checkBalance(){
balanceRef.on('value', function(snapshot) {
if(snapshot.val()=== null){
mLeft=0
} else{
mLeft=snapshot.val().tokensLeft
}
return mLeft
})
}
When the user clicks a button I 1st check their balance
$('#grabbit').click(function() {
//call checkBalance function on click
var myTokensLeft=checkBalance();
});
the problem here is it's returning the function value as NaN or undefined
I've tested repeated, the function does return a value. Why can't It pick up the value on click? Am I doing something wrong with the structure. I'm very new to javascript

balanceRef.on looks like an async function to me, which is why you won't get a return value.
try something like this:
//check user balance
function checkBalance(callback){
balanceRef.on('value', function(snapshot) {
if(snapshot.val()=== null){
mLeft=0
} else{
mLeft=snapshot.val().tokensLeft
}
callback(mLeft);
})
}
$('#grabbit').click(function() {
//call checkBalance function on click
checkBalance(function(myTokensLeft){
// do something with myTokensLeft
});
});
EDIT: more details
the function balanceRef.on does not return your desired data, it only attaches the function function(snapshot)... to the object balanceRef's value event.
When the object balanceRef's value event gets triggered (i.e. data is available), your function function(snapshot)... gets executed, but it's return values goes nowhere, because the javascript parser has already moved on.
try this:
function checkBalance(){
balanceRef.on('value', function(snapshot){
return 'foo';
});
return 'bar';
}
$('#grabbit').click(function(){
console.log(checkBalance());
// this will print 'bar' in your
// in your console, not 'foo'
});
EDIT 2: to avoid callback chaining, one can use a variable that will be common to both functions. (not a very good practice though, since you cannot be sure that the value has been set, when you want it!)
(function(){
// added the closure to avoid myTokensLeft cluttering
// into global variables
var myTokensLeft;
balanceRef.on('value', function(snapshot){
var mLeft;
if (snapshot.val()=== null){
mLeft=0
} else{
mLeft=snapshot.val().tokensLeft
}
myTokensLeft = mLeft;
});
$('#grabbit').click(function(){
// do something with myTokensLeft
});
}());

It seems to me that on your click event you only create another listener to "value" event and gods know how and when that event is triggered and where the function returns your mLeft.
What is happening in your code is that after each click on "#grabit" always new listener is created, but the function inside is not necessarily to be called and if it is called it is called asynchronously which means by the time it is finished you already got "undefined".

Related

Accessing a JavaScript / jQuery Instance only once

I have a jQuery function which is called on several events (button click, change etc.)
This function is called in the documentReadyFunction and is feeded with start values..
everytime I call this function, parameters will be passed to the function.
My problem is: I don't want to create a new Object each time I call the function, because if I set a variable which decides if a part of the function is beeing executed or not, will be always overwritten..
What do I have to do, to access the first created instance instead of creating always a new one with every function call..
Down below is a simplyfied version of my function.. Maybe you understand my problem better then.
$.fn.doSomething = function(param1) {
var localParam = param1;
var amIcalledMoreThanOnce = parseInt(0, 10);
if (param1 == 1) {
amIcalledMoreThanOnce = amIcalledMoreThanOnce + 1;
if (amIcalledMoreThanOnce == 1) {
$('#run').val(amIcalledMoreThanOnce);
// fill form fields with URL parameters
// This shall be executed only once after getting the URL vals
} else {
// set the localParam to 0 to exit this loop and reach the outter else..
localParam = 0;
$.fn.doSomething(localParam);
}
} else {
$('#run').val(amIcalledMoreThanOnce);
// use the User Input Data not the URL Params
}
};
$.fn.doSomething(1);
$.fn.doSomething(1);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<input type="number" id="run">
you can use this pattern:
var nameOfYourFunction = (function() {
var initializedOnlyOnce = {};
return function() {
//use initializedOnlyOnce here.
}
})();
here what you see is; you create and run a function immediately when the code is run. The outer function immediately returns the inner function and it's assigned to nameOfYourFunction. Then you can use the nameOfYourFunction(); just as any other function. However any varible declared in the outer function will be available to the nameOfYourFunction() and initializedOnlyOnce will never be initialized again.

How to return variable value from jquery event function?

Can I get value from jquery event function?
I want to get something like this:
function Init() {
var my_variable = $some_object.click(function() {
var my_variable = 'value';
return my_variable // I need to get the variable value from click event
// but I don't know ways to do it
});
console.log(my_variable); // I want to have possibility to use the value here
}
No.
The function that sets the event handler will have finished running before the event handler runs.
No, you can't do that. You can only capture the value in a variable defined outside the scope of your handler:
function Init() {
var my_variable = '';
$some_object.click(function() {
my_variable = 'value';
});
console.log(my_variable); //Prints empty string
$some_object.trigger('click');
console.log(my_variable); //Prints 'value'
}
The answer to your question is No!. You cannot return a value from event handler.
Now next thing which made you curious is how to get some data which comes from the event handler itself. This is where event propagation comes and passing some data along with that.
Checkout below fiddle which shows how data which was created in event handler of button click, is being passed for any custom purpose.
http://jsfiddle.net/kowmwq9j/
Below is simple html and understandable javascript code
<div id='parent'>
<button id='test'>CLick</button>
</div>
var a=document.getElementById('test');
a.addEventListener('click', function(){
var str='sample';
//creating a custom event with name 'build' and passing object as data
var event = new CustomEvent('build', { 'detail': str });
//dispatches the event which invokes the affected listeners
this.dispatchEvent(event);
})
//Listening for 'build' event
document.getElementById('parent').addEventListener('build', getData, true);
function getData(e){
alert(e.detail);
}
Links shared which can help one to understand the concept. dispatchEvent & createCustomEvent .Hope that helps to answer your question!
Added some comments & links which will help those who aren't aware of the functions & behavior used.
If I understand the question maybe you can do something like this...
Variable = 5 is used within 2 functions...each function does something with it's value...
the last function uses the totals of the first 2 functions and again does something.. So the point is that u can access the 2 totals variables and use them in the third function... As long as you set your function variable as global...so don't put VAR in front of it. If you set var in front of it u are saying that your variable is private for that function...
Change the value of the original variable and all the values changes ....
variable = 5; // this is a global variable
source = $('div#een h2'); // put your source here
function Init(){
source.on('click', function(){
Total1 = variable * 2;
console.log(Total1); // It outputs 10
});
};
Init();
function Init2(){
source.on('click', function() {
Total2 = variable * 5;
console.log(Total2); // It outputs 25
});
};
Init2();
function Init3(){
source.on('click', function() {
Total3 = Total1 * Total2; // here we use the 2 Totals...
console.log(Total3); // outputs the Total of both totals... should be 250
});
};
Init3();

create a javascript function programmatically

Need this for the youtube api // the onStateChange callback functions!
I want to programmatically create functions which will listen to the "onStateChange" event emitted by several youtube player. Adding the listener works already:
function onYouTubePlayerReady(playerId) {
var ytpStateManager = playerId +"_StateManager";
document.getElementById(playerId).addEventListener("onStateChange", ytpStateManager );
...
The function I need to create based on the playerId variable ("ytp_1", "ytp_2", ...) is
function ytpStateManager(newState) {
ytpStateHelper(playerId , newState);
}
So the result for the playerId "ytp_1" would look like this:
function ytp_1_StateManager(newState) {
ytpStateHelper("ytp_1", newState);
}
Works also but right now I need to add them manually for each player, which is not what I need. I want to create them automatically when a new player sends a readyState event.
My problem is that it seems like these functions need to be a global functions to work properly. I tried several options for days now. My problem is that I do not know how (if there is a way) to define a global function, incl. the function name, programmatically, based on another variable.
Its a bummer that the ytp does not emit an event which includes the state AND the player/target. Would make things much easier. All this is basically the workaround as I need all to do stuff on all stateChanges.
If there is a better/simpler way, PLEASE let me know :) Otherwise a solution for this question is highly welcome.
Maybe there is a way to rerout the event, to make it more "accessible"?
I read in the spec that .addEventListener also takes a object, so I tried to bind the event to a dedicated object. But again, it did not get triggered. Feels like I tested everything ...
UPDATE
I am now switching to the iframe player (from swfobject) because that one provides an event which includes playerId and state :D Yeahhh!! After spending week with the wrong ytplayer this feels like a great advancement. Also seems like yt wants us to use the iframe player which can dynamically use html5 when supported.
You create a function that returns a function:
function createStateManager(playerId) {
return function (newState) {
ytpStateHelper(playerId , newState);
}
}
Then you call your function factory when setting up the event listener:
var player = document.getElementById(playerId);
player.addEventListener("onStateChange", createStateManager(playerId));
DEBUGGING
I'm not sure why that's not working, but here is a debugging suggestion. I suspect you may not be getting the playerId on your onYouTubePlayerReady handler.
function onYouTubePlayerReady(playerId) {
console.log('Player ready. The player id is: ' + playerId);
var ytpStateManager = playerId +"_StateManager";
var player = document.getElementById(playerId);
player.addEventListener("onStateChange", createStateManager(playerId));
}
function createStateManager(playerId) {
return function (newState) {
console.log('State changed for player ' + playerId + '. New state is ' + newState);
ytpStateHelper(playerId , newState);
}
}
Could you try that, and post what you get from both console.log calls?
1)You can create Function object new Function([params], "BODY")
So you can combine body of your function as string variable and put into as BODY
Example:
var twoNumAverage = new Function("x", "y", "return (x + y)/2")
console.log(twoNumAverage(3,7))
2)And new can create dynamically name and BODY
Example
var globalObject ={};
var nameFn ='MyNewFunction';
var createFn = function(object,functionName, Body){
object[functionName]= new Function(Body);
}
createFn(globalObject,nameFn,"return (arguments[0] + arguments[1])/2");
You can call your new function:
globalObject[nameFn](10,20);
Result: 15
Please note that in body your function you can get params via collection arguments
window["foo"+"bar"] = function(){ console.log("foobar is called"); }
Here's a way to create a named proxy function that executes another function with the context you supply.
function createNamedProxy(name, fn, context) {
var template = [
'(function #name() {',
' #name.fn.apply(#name.context || window, arguments);',
'})'
].join('').replace(/#name/g, name),
result = eval(template);
result.fn = fn;
result.context = context;
return result;
}
// Example Usage
var anonymous = function() { alert( document === this ); },
named = createNamedProxy('Named', anonymous, document);
// Will alert 'true'
named();
The solution above creates a function that can create and return a named function that executed whatever you'd like. If you don't supply context, it will assume the window object just like a normal anonymous function would. To create the solution you wanted you would do:
var varName = 'ytp_1';
window[varName + '_StateManager'] =
createNamedProxy(varName + '_StateManager', function(newState) {
ytpStateHelper(varName, newState);
});
Where varName could be any programmatic prefix you'd like. When invoking ytp_1_StateManager() you would pass in your newState value and the code would call ytpStateHelper with your variable name and the newState.
Hope this helps.

Titanium mvc - call function and wait for result

I am currently in the process of making my first Titanium iPhone app.
In a model I got:
(function() {
main.model = {};
main.model.getAlbums = function(_args) {
var loader = Titanium.Network.createHTTPClient();
loader.open("GET", "http://someurl.json");
// Runs the function when the data is ready for us to process
loader.onload = function() {
// Evaluate the JSON
var albums = eval('('+this.responseText+')');
//alert(albums.length);
return albums;
};
// Send the HTTP request
loader.send();
};
})();
and I call this function in a view like:
(function() {
main.ui.createAlbumsWindow = function(_args) {
var albumsWindow = Titanium.UI.createWindow({
title:'Albums',
backgroundColor:'#000'
});
var albums = main.model.getAlbums();
alert(albums);
return albumsWindow;
};
})();
however it seems like the call to the model (which fetches some data using HTTP) doesn't wait for a response. In the view when I do the alert it haven't received the data from the model yet. How do I do this in a best-practice way?
Thanks in advance
OK,
Something like this,
function foo(arg1, callback){
arg1 += 10;
....
... Your web service code
....
callback(arg1); // you can have your response instead of arg1
}
you will call this function like this,
foo (arg1, function(returnedParameter){
alert(returnedParameter); // here you will get your response which was returned in above function using this line .... callback(arg1);
});
so here arg1 is parameter (simple parameter like integer, string etc ... ) and second argument is your call back function.
Cheers.
What you need is Synchronous call to web service, so that it will wait till you get the response from the service.
To achieve this in java script you have to pass callback function as parameter and get the return value in callback function instead of returning value by return statement.
Actually coding style you are using is new for me because i am using different coding style.
But the main thing is you have to use call back function to retrieve value instead of return statement. Try this and if you still face the problem than tell me i will try to give an example.
the callback way like zero explained is nicely explained, but you could also try to get it handled with events.
(function() {
main.ui.createAlbumsWindow = function(_args) {
var albumsWindow = Titanium.UI.createWindow({
title:'Albums',
backgroundColor:'#000'
});
var status = new object(), // eventlistener
got_a_valid_result = false;
// catch result
status.addEventListener('gotResult',function(e){
alert(e.result);
got_a_valid_result = true;
});
// catch error
status.addEventListener('error',function(e){
alert("error occured: "+e.errorcode);
git_a_valid_result = true;
});
var albums = main.model.getAlbums(status);
// wait for result
while (!got_a_valid_result){};
return albumsWindow;
};
})();
and your model may something like
main.model.getAlbums = function(status) {
var loader = Titanium.Network.createHTTPClient();
loader.open("GET", "http://someurl.json");
loader.onload = function() {
var albums = eval('('+this.responseText+')');
status.fireEvent('gotResult',{result:albums});
return albums;
};
loader.onerror = function(e){
status.fireEvent('error',{errorcode:"an error occured"});
};
// Send the HTTP request
loader.send();
};
Just as a suggestion, try to use JSON.parse instead of eval as there are risks involved with using eval since it runs all javascript code.
I think that the solution The Zero posted is likely better for memory management, but I'm not totally sure. If you do and eventListener, be aware of the following
(see https://wiki.appcelerator.org/display/guides/Managing+Memory+and+Finding+Leaks)
function doSomething(_event) {
var foo = bar;
}
// adding this event listener causes a memory leak
// as references remain valid as long as the app is running
Ti.App.addEventListener('bad:idea', doSomething);
// you can plug this leak by removing the event listener, for example when the window is closed
thisWindow.addEventListener('close', function() {
// to remove an event listener, you must use the exact same function signature
// as when the listener was added
Ti.App.removeEventListener('bad:idea', doSomething);
});

How to lock AJAX functions from overlapping?

I've got one function checkEvery15Seconds that runs every 15 seconds. It checks to see if new comments have been added to a page.
I've got a form that submits a new comment once the submit button is pressed, then displays the new comment on the page.
In the process of adding a new comment checkEvery15Seconds is querying the database at the same time, so I end up with duplicate comments on the page (not in the database though, this is purely a JavaScript issue).
How can I get my "submitComment" function to stop checkEvery15Seconds and restart it after the "submitComment" function has finished executing?
add a boolean called somewhat suspend15sCheck in a scope which is accessible by both functions. enable it while adding the comment and afterwards set it to false again.
in your 15sCheck-function you first have to check if you are allowed to check :-)
var suspend15sCheck = false;
function addComment()
{
suspend15sCheck = true;
// add comment on base of form data
suspend15sCheck = false;
}
function myTimer()
{
if(suspend15sCheck === false)
{
// add comments via ajax request
// remember to check if the comments who will be added already exist :-)
}
}
Simplest solution: use a flagging variable that you turn on and off. The first line of your "checkEvery15Seconds" function reads: if (!global_checkingEvery15Seconds) return;
Just set that variable (whatever you name it, global or object-bound) to true when you want the checking turned on, and off when you don't.
You'll need a status variable to indicate the current state of the comment ajax request
var requestComments = false;
if(requestComments === false) {
requestComments = true;
// make ajax request
// on ajax success/fail
requestComments = false;
}
Wrap it up in an object that allows other functions to set start/stop flags on it.
function My15SecondsObj() {
var objSelf = this;
//
this.run();
}
My15SecondsObj.Paused = false;
My15SecondsObj.prototype.run= function() {
if (!Paused)
{
// Do your call here
}
var _this = this;
setTimeout(function() { _this.run(); }, 15000);
}
Now when you want to use this object, just do
var myObj = new My15SecondsObj();
and when you want to pause it,
myObj.Paused = true;
and start it again by doing:
myObj.Paused = false;
Add some events if you want to get really crazy, so that other objects can subscribe to notifications about when the database updates have succeeded, etc...

Categories

Resources