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.
Related
This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 1 year ago.
I am trying to get a shared key based on whether it already exists in the db or not. I am using Firebase store database.
The problem is that even though i assign passkey some value inside the sub-function, when I do console.log outside the function, it just prints the original passkey which was set at declaration.
I have tried using window.passkey and window.global.passkey by declaring passkey as global variable, outside all functions, but it didn't work.
I am working with a node.js project. My code is as follows:
// var passkey = {"shared_key":""} // Declaring passkey as global variable but didn't work.
const onSubmit = (formData) => {
const email_id = getUserDetails().email;
const from_db = db.collection(email_id);
const to_db = db.collection(formData.to);
var passkey = {"shared_key": ""};
// Check if there exists a shared key between `from` and `to`
// Checking for shared key in either of the db is enough.
to_db.doc(email_id).get().then((res) => {
// assume that res.exists is true.
if (res.exists) {
passkey.shared_key = res.data().shared_key; // passkey is set here
} else {
// Generate and use a shared key
...some code to generate a shared key ...
passkey.shared_key = generated_key;
}
});
console.log(passkey);
// above prints {"shared_key": ""} instead of printing the value of shared key taken from the database.
// or the one that is generated!
};
I know it is related to variable hoisting in javascript but I think there has to be some workaround.
Any help/comment is appreciated. Thanks in advance.
When you code with this pattern
function doIt () {
var something = ''
invokeFunction()
.then (function handleResult (result) {
console.log('2', something)
something = result
} )
console.log('1', something)
}
your console.log('1', something) runs before the inner function (I named it handleResult for clarity) is ever called. That's because invokeFunction() is asynchronous; it returns a Promise object.
You could rewrite this as follows:
async function doIt () {
var something = await invokeFunction()
console.log('1', something)
}
to get the kind of result you want.
With respect, you will have a very hard time coding Javascript unless you take the time to learn about async / await and Promises. In that respect the Javascript language is very different from other languages. Drop everything and learn that stuff now. Seriously.
I've been trying to understand async, promises, etc. and I think I have a basic understanding of it, but I'm not getting the results I expect.
I have a HTML table, with the following:
<table data-bind="visible: viewPrincipal()">
viewPrincipal() is a function that should return true or false. This does work at the most basic level if viewPrincipal() just consists of return false or return true. But what I'm trying to do is call an async function to get the true or false value from there.
function viewPrincipal() {
console.log("Seeing if person is in principal group");
return IsCurrentUserMemberOfGroup("Principal Members", function (isCurrentUserInGroup) {
console.log(isCurrentUserInGroup);
return isCurrentUserInGroup;
});
}
The console.log works, and returns a true or false as I'd expect it to. But I want the parent viewPrincipal() function to return that true or false value, and all I get is "undefined".
I understand why this is happening - the IsCurrentUserMemberOfGroup() function is taking a bit of time to complete - but I don't know how to fix it. I know how to chain functions together, but when I'm trying to use something like knockout.js to determine if a table should be visible or not, I don't know how to chain.
Can anyone help?
The best way is to use an observable bool, and let your a-sync function change it's value. Let the magic of two-way-bindings do the rest.
Example:JSFIDDLE
function vm() {
this.viewPrincipal = ko.observable(false);
};
var vm = new vm();
ko.applyBindings(vm);
function fakeAsync() {
setTimeout(() => {
vm.viewPrincipal(true);
}, 1500);
}
fakeAsync();
I am a bit lost with your approach, but I'll try to help.
First, please double-think whether you really want to implement access control on the client side. Simply hiding an element if the user does not have sufficient rights is pretty dangerous, since the (possibly) sensitive content is still there in the DOM, it is still downloaded, all you do like this is not displaying it. Even a newbie hacker would find a way to display it though - if nothing else he can simply view it using the F12 tools.
Second, is that triple embedding of functions really necessary? You have an outermost function, that calls a function, which, in turn, calls the provided callback. You could clear this up by using computed observables:
function viewModel() {
var self = this;
var serverData = ko.observable(null);
this.viewPrincipal = ko.computed(function() {
var srvDataUnwrapped = serverData(); // access the inner value
if (!srvDataUnwrapped) {
return false;
}
// Do your decision logic here...
// return false by default
return false;
});
// Load the permission details from the server, this will set
// a variable that the viewPrincipal depends on, this will allow
// Knockout to use its dependency tracking magic and listen for changes.
(function() {
$.ajax(url, {
// other config
success: function (data) {
serverData(data);
}
);
})();
};
var vm = new viewModel();
and then in your view:
<table data-bind="visible: viewPrincipal">
note the lack if ()'s here, it is an observable, so Knockout will know how to use it.
If this seems overly complicated to add to your already existing code, then you could simply define an observable instead, and set the value of that inside your callback:
function viewModel() {
// other stuff ...
this.viewPrincipal = ko.observable(false);
// Call this wherever it fits your requirements, perhaps in an init function.
function checkPrincipal() {
IsCurrentUserMemberOfGroup("Principal Members", function (isCurrentUserInGroup) {
viewPrincipal(isCurrentUserInGroup);
});
};
};
With this approach, the markup would be the same as in the previous one, that is, without the parentheses:
<table data-bind="visible: viewPrincipal">
Doing it this way will simply set the inner value of an observable inside the callback you pass to IsCurrentUserMemberOfGroup, and because Knockout is able to track changes of observables, the value change will be reflected in the UI.
Hope that helps.
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.
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.
So, I have a script that fetches data from a SQL database and I'm trying to build a JS wrapper for it. I'm using the following functions to call that script and use information from the DB as soon as it's ready.
var User = function() {
this.email = null;
//Async call to get user values
this.current(function(response) {
this.email = response.email;
//The value is assigned/usable at this point
});
};
User.prototype.current = function(callback) {
$.post("php/db_functions.php", {object: "CurrentUser"}).done(function(result) {
callback(JSON.parse(result)[0]);
});
};.
Everything seems to work fine, but if I try to access the value from the object after I've created it, it returns undefined, like so:
var me = new User();
//And then, way after the async call and when me.email should be defined
me.email //Returns undefined
Why can I use it in the callback, but not afterwards?
In a function, the context variable this points to either the global window object or to undefined in the strict mode, unless specified otherwise by the caller. Therefore, you need to either capture the value of this in a local variable:
//Async call to get user values
var that = this;
this.current(function(response) {
that.email = response.email;
});
or call the function in the desired context using either the call or the apply method:
User.prototype.current = function(callback) {
var that = this;
$.post("php/db_functions.php", {object: "CurrentUser"}).done(function(result) {
callback.call(that, JSON.parse(result)[0]);
});
};.
Also, as others have mentioned, there is no guarantee the AJAX request will have finished by the time the User contructor returns.
This is a timing bug since the variable is not assigned until the async call returns. You can't access email right away.