Javascript updating global variables and hoisting - javascript

I have a javascript function that uses an ajax function to post to a php script and a variable used to determine if the result comes back as true or false. In php, I would normally assign the function to a variable such as this:
$insert_item = $process->insert_item($item_array);
If ($insert_item->error)
{
//error detected, do something
}
I have not been able to accomplish this with javascript. Instead, I get a [object object] return if I assign a function to a variable. As a cheap alternative, I am trying to use a global variable to write any errors:
var error = false;
function update_db(formInput) {
$.post(action.php, formInput, function(data) {
if (data != 0) {
error = true
}
});
return error;
}
var updateDb = update_db(form_data);
if (updateDb) {
alert("error detected");
In this example, 'error' comes back as false despite the ajax function updating it to true. I have read all about javascript hoisting, but have yet to find a solution. Is there anyway around this? My problem stems completely from the ajax function which I have also tried accessing directly to return any vars (like I easily do in PHP) but I have had no luck!
As a side note, I find it interesting that I can access 'error' within the ajax function (returns as false) but not able to change it.

Related

How to get chrome storage values via local getter setter class?

I have created a local class in a JavaScript file with following content:
class CustomChromeStorage {
//#region userName
get userName() {
let isCurrentValueSet = false;
chrome.storage.sync.get('userName', function (obj) {
this._userName = obj;
isCurrentValueSet = true;
});
while (true) {
if (isCurrentValueSet) {
return this._userName;
}
}
}
set userName(newValue) {
this._userName = newValue;
chrome.storage.sync.set({ 'userName': newValue }, function () {
});
}
remove_userName() {
this._userName = null;
chrome.storage.sync.remove('userName', function () {
});
}
//#endregion userName
My Idea to do such type of code is when I write somewhere else in my code like:
alert(new CustomChromeStorage().userName);
Then my code simply fetches username from chrome storage and show it via an alert. In order to fetch a value from chrome storage we need to provide a callback with as parameter the value. I know this is good practice for asynchronous process but it sometimes becomes cumbersome for me to handle all the callbacks.
I want that when I fetch value from chrome storage via my custom class to execute current code asyncronously. This is why I have written infinite while loop inside getter method of that property but the problem is when I try to alert username via custom chrome storage class my total program execution becomes hang.
The reason behind it is that I initially set isCurrentValueSet = false which never gets true inside while loop.
If anybody have any idea why it does not set to true inside while loop then please let me know.
The obj returned from sync.get is {userName: value} - use obj.userName.
The reason isCurrentValueSet doesn't get set to true is because the function is asynchronous - when the callback executes, it doesn't have access to the class variable isCurrentValueSet.
What you're trying to achieve is just wrong. It's a fact that storage requests are asynchronous for the good of the user and browser performance. You have to learn to design around it and it's easy enough when you get used to it.
You can retrieve multiple variables in one hit so if you have a section of code that needs several variables, just do:
chrome.storage.sync.get({a:"",b:"",c:0,d:[]}, function(result) {
a = result.a
b = result.b
c = result.c
d = result.d
your code
});
By passing an object in, you can request multiple variables and define defaults for if they don't yet exist in storage. Of course you don't have to extract the variables.

meteor.js - how to check values from asynchronous callback

CONTEXT
Im making a call which, if successful, changes the value of a boolean from false to true. Then, outside of this call, I check if this boolean is true and, if so, I route to another page.
PROBLEM
Console logs indicate that the if statement which checks the boolean's value is being executed before the calls have had a time to change the boolean's value. I realize that this is because of asynchronicity, but not sure what the correct design pattern for this would be. Here is a snippet:
//set variables to check if the even and user get updated or if error
var eventUpdated = false;
Meteor.call('updateEvent', eventId, eventParams, function(error, result){
if(error){
toastr.error(error.reason)
} else {
var venueId = result;
toastr.success('Event Info Updated');
eventUpdated = true;
console.log(eventUpdated)
}
});
console.log(eventUpdated)
if (eventUpdated) {
Router.go('/get-started/confirmation');
}
POSSIBLE SOLUTIONS
I'm guessing I need a way to hold the if statement from being executed until the callback returns a value. Based on Googling, I think this has something to do with this but not too clear on how to actually use it.
Since the conditional is run before the callbacks have returned a value, you need a conditional that is inside a function that is being run reactively. I used the following code:
Tracker.autorun(function(){
if (Session.get('userUpdated') && Session.get('passwordUpdated') && Session.get('eventUpdated')) {
Router.go('/get-started/confirmation');
}
});
You can read more about Meteor reactivity here.
Nope. The issue is that since it's an async function, this:
console.log(eventUpdated)
if (eventUpdated) {
Router.go('/get-started/confirmation');
}
Runs before the actual call. Use a Session.set inside the call like this:
Session.set("eventUpdated", "true");
And then outside:
eventUpdated = Session.get("eventUpdated");
console.log(eventUpdated)
if (eventUpdated) {
Router.go('/get-started/confirmation');
}
Since Session is a reactive variable you should get the current value correctly.

Forbid ajax calls in some parts of the code

For our internal js framework, to ensure good usage of the framework, I want to forbid the usage of Ajax requests in some parts of the framework.
Is there a way to achieve something similar to this:
function doSomething() {
instructions with ajax calls...
withAjaxForbidden(function() {
instructions using ajax calls should raise exception here
});
instructions with ajax calls...
}
Is it possible to implement something like withAjaxForbidden?
Note that obviously I expect the ajax system to be leaved in a consistent state in case an error is thrown.
Just create your own ajax function and make it dependent on some global variable. After that you can just remove the regular ajax functions from the scope and you're done :)
var ajaxEnabled = true;
function withAjaxForbidden(f){
ajaxEnabled = false;
f();
ajaxEnabled = true;
}
function ajax(...){
if(!ajaxEnabled)throw 'Ajax requests are forbidden within this block';
...
}
Here's a fiddle as an example: http://jsfiddle.net/u98b7bk3/1/
<div id="console">
No messages yet
</div>
<script type="text/javascript">
// Disable Ajax globally, to keep it working for your own library make
// sure you save this locally before overwriting
XMLHttpRequest = undefined;
</script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
// Try an ajax request with jQuery
$.ajax('/').always(function(data, status, error){
$('#console').html("data: " + data + "<br>status: " + status + "<br>error: " + error);
});
</script>
Local variables override global variables within the function's scope. Simply create a variable for each native and library ajax function and set it to undefined from within your protected function. It will be prevented from being able to make use of the functions and variables by the same name. As long as they are set using "var" it should only affect the function's scope and not the global scope.
Edit: This may not be exactly what you were looking for, but as far as I know the precise way you want to accomplish this is not possible due to variable scoping but if you don't mind the extra code around your AJAX-restricted code you can use this method. Unlike the other answer this will work asynchronously and does not affect global variables, which makes it much less likely that problems will occur from the use of this code:
Edit 2 (with jQuery support):
var testFunction = (function($){
// Global to local/lexical:
var XMLHttpRequest = undefined;
var eval = undefined;
var setTimeout = undefined;
var setInterval = undefined;
var Function = undefined;
var window = undefined;
$ = (function($){ if($) {
var newjq = function(s, c) {
// Reroute main function
return $(s, c);
};
var jQueryBlacklist = {
// Initialize blacklist
"ajax": true,
"post": true,
"get": true,
"getJSON": true,
"getScript": true
};
for(i in $) // Reconstruct Object
if($.hasOwnProperty(i)
&& !jQueryBlacklist[i])
newjq[i] = $[i];
return newjq;
} }($));
// Real testFunction() below:
return function() {
// AJAX-forbidden code
// $.ajax should be undefined
}
}($));
// $.ajax should work normally here
testFunction(); // not in here
// $.ajax should work normally here
Click here to see the fiddle

Deferred timing issue

I have the following code I've designed to load and run script at runtime. You'll note that I save it to localStorage if it isn't already there. Now it runs fine if it's stored there already, but when it's just got the text from the file it throws ReferenceError: loginLaunch is not defined, though the text seems to have been loaded (hence the console.log lines that check the length). For your convenience I've included a line, localStorage.clear();, to make it alternate between the error message that's the problem and ReferenceError: loginLaunch is not defined, which given the code below is the desired result.
I don't understand why it should work one way and not the other. If it's a timing issue I don't see how the use of the promise, loginCode, lets it through unless possibly appendChild() is asynchronous, but I'm under the impression that it isn't (mainly because it has no callback, and I tried to find out, but could not) and even then why would code before the appendChild() have an impact?
Have I messed up one of the promises? I include the contents of the file login.js at the end. I searched SO for anything relevant but without any luck except for just one post that states that appendChild is synchronous.
Please help.
var loginCode = runCode("login_1001","./js/login.js");
loginCode.done(loginLaunch());
//FUNCTIONS START HERE
function getCode(local, source) { //This creates the promise to get the code (not to run it)
console.log("start of loadCode");
dfd = $.Deferred(); //This is the one to return.
script = localStorage.getItem(local); //Try to load from local storage.
// console.log("script after local attempt: "+script);
if (script) { //If found...
console.log("found Local code");
dfd.resolve(script);
localStorage.clear(); //Added for debugging
} else { //load from file.
ajax = $.ajax({
url : source,
cache : false,
dataType : "text", //load as text initially so that we can store it locally.
});
ajax.done(function(fromFile){
localStorage.setItem(local, fromFile); //store it locally.
//console.log("script after ajax attempt: "+script);
dfd.resolve(fromFile);
});
ajax.fail(function(){
dfd.reject("Error retrieving code. You may be disconnected");
});
}
return dfd.promise();
}
function runCode(local, source) {
dfd = $.Deferred(); //This is the one to return.
code = getCode(local, source); //local promise
code.done(function(retrievedCode){
console.log(retrievedCode.length);
var head = document.getElementsByTagName('head')[0]; //first head section
var el = document.createElement("script"); //named the same as the local storage
//script.type= 'text/javascript'; Redundant — it's the default
// el.id = local; //Probably redundant, but if we want to manipulate it later...
el.text = retrievedCode;
head.appendChild(el); //This shouldn't run anything, just make global functions that will be called later.
console.log(el.text.length);
dfd.resolve(); //If we need to return the node to manipulate it later we'd make the variable above and 'return' it here
});
return dfd.promise();
}
Here's the contents of the login.js file.
function loginLaunch(){
dfd = $.Deferred(); //This is the one to return.
loadElement("login.1001", "#content", "login.html");
//After the element has been loaded we have a disconnect — i.e. there's no promise waiting, so we have to wait for the user.
}
$("#content").delegate('#loginButton','click',function(){
console.log("Login click");
//php to pick up the entered details and pass them to common php that also uses the
checkCredentials = $.ajax({
type : "POST",
url : "./php/credentials.php",
data : {
queryString : queryString
},
datatype : "text", // 1 or 0
});
checkCredentials.done(credentialsChecked(success));
// MOVE THIS STUFF
readyPublicList();
$.when(publicListCode,loggedIn).then(runDefaultPublicList()); //Assumes successful login so it loads the code for the list window in parallel.
//Note that it's probable that my approach to the login window may change, because it needs to be available on the fly too.
// $("#content").html("<p>test</p>"); //Successfully tested, well it was once.
});
function loginHide(){
$("#loginHtml").hide;
}
I'm not sure why this works:
var loginCode = runCode("login_1001","./js/login.js");
loginCode.done(function(){loginLaunch();});
and this doesn't:
var loginCode = runCode("login_1001","./js/login.js");
loginCode.done(loginLaunch);
My one thought is that maybe if you pass literal named functions to .done then they are validated when loginCode is created, while anonymous functions aren't validated until they are about to be run.
I should note that the error was appearing before the console.log output.
Maybe someone with a better grasp of the technicalities can clarify. For now I'm just happy to stop tearing my hair out, but I like to know how things work...
You need to change at least three things. First change this:
loginCode.done(loginLaunch());
to this:
loginCode.done(function() {loginLaunch()});
You need to be passing a function reference to the .done() handler so it can be called later. The way you had it, you were calling it immediately BEFORE loginCode() was done with its work, thus it was getting called too early.
In addition, loginLaunch doesn't exist yet so you can't pass a reference directly to it. Instead, you can pass a reference to a wrapper function that then calls loginLaunch() only after it finally exists.
And second, you need to declare your local variables with var so they aren't implicit globals and stomp on each other. For example, you have multiple functions who call each other trying to use the same global dfd. That is a recipe for disaster. Put var in front of it to make it a local variable so it's unique to that scope.
And third, el.text doesn't look like the right property to me for your script. Perhaps you meant to use .textContent or since you have jQuery, you can do:
$(el).text(retrievedCode);
In a couple style-related issue, ALL local variables should be declared with var before them so they are not implicit globals. This will bite you hard by causing mysterious, hard to track down bugs, even more so with async code.
And, you can generally use the promise returned by jQuery from ajax functions rather than creating your own.
To incorporate those improvements:
runCode("login_1001","./js/login.js").done(loginLaunch);
function getCode(local, source) { //This creates the promise to get the code (not to run it)
var script = localStorage.getItem(local); //Try to load from local storage.
if (script) { //If found...
localStorage.clear(); //Added for debugging
// return a resolved promise (since there's no async here)
return $.Deferred().resolve(script);
} else { //load from file.
// return the ajax promise
return $.ajax({
url : source,
cache : false,
dataType : "text", //load as text initially so that we can store it locally.
}).then(function(fromFile){
localStorage.setItem(local, fromFile); //store it locally.
return fromFile;
});
}
}
function runCode(local, source) {
return getCode(local, source).then(function(retrievedCode){
console.log(retrievedCode.length);
var head = document.getElementsByTagName('head')[0]; //first head section
var el = document.createElement("script"); //named the same as the local storage
$(el).text(retrievedCode);
head.appendChild(el); //This shouldn't run anything, just make global functions that will be called later.
console.log(el.text.length);
});
}
FYI, if you just want to insert a script file, you don't have to manually retrieve the script with ajax yourself. You can use the src property on a script tag and let the browser do the loading for you. You can see a couple ways to do that here and here.

Javascript variable scope question

I am confused with the variable scope in Javascript. I am trying to load data file (.json) using Prototype library and parse the response text using json-sans-eval. The problem is it seems to me that the content from data file is lost if I tried to access "dataObj" outside of the Ajax.Request scope.
The variable in Javascript has reference count. I don't understand how can the global variable 'dataObj' will lose its data. Any hint or help?
Thank you!
var dataObj;
function OnLoadHandle() {
new Ajax.Request('data.json',
{
method:'get',
onSuccess: function(transport)
{
var response = transport.responseText || "no response text";
dataObj = jsonParse(response);
console.log(dataObj["[0,0]"]); // OK
},
onFailure: function(){ alert('Something went wrong...') }
});
console.log(dataObj["[0,0]"]); // ERROR
}
The second invocation of console.log(...) at the end of OnLoadHandle runs immediately, whereas the one inside onSuccess runs only after the request completes. The second console.log serves no purpose.
More broadly, this means that there is no point in making dataObj a global variable. It is only useful after it is assigned in onSuccess and should therefore be scoped as a local variable within onSuccess. If you need it elsewhere, onSuccess should pass it as a parameter to some other function.

Categories

Resources