This works
chrome.storage.local.get('sizePref', function(items) { // Get size preferences from storage
var sizePref2 = items.sizePref.tops; // Set size to a var
console.log("You can get this " + sizePref2)
});
However, when I try to make it a function
function getSize(itemSize) {
chrome.storage.local.get('sizePref', function(items) { // Get size preferences from storage
var sizePref = items.sizePref.itemSize;
return (sizePref);
});
}
var mySize = getSize(tops);
console.log("This size that u are looking for is " + mySize)
it says "tops" is undefined.
When the property name is in a variable, you use the bracket syntax. So, instead of this:
items.sizePref.itemSize
you use this:
items.sizePref[itemSize]
In addition, you cannot return a value synchronously from an async callback. That logic is just wrong. So, you can't make a function getSize() that will return the result. The result will not be available until some time LATER after getSize() already returns. You would have to either pass a callback into getSize() or have getSize() return a promise.
function getSize(itemSize) {
return new Promise(function(resolve) {
chrome.storage.local.get('sizePref', function(items) { // Get size preferences from storage
resolve(items.sizePref[itemSize]);
});
}
getSize("whatever").then(function(result) {
// code that uses the result here
});
If you are not familiar with promises (viz. unlikely) then you can use callbacks too. I personally think promises are a better way to solve your issue.
function getSize(itemSize, callback) {
chrome.storage.local.get('sizePref', function(items) {
// Get size preferences from storage
var sizePref = items.sizePref[itemSize]; //thanks #jfriend00
callback (sizePref); //thanks at #Kevin Friedheim
});
}
var mySize = getSize(tops, function (mySize) {
console.log("This size that u are looking for is " + mySize)
});
Related
I have a function that fetches data from a database:
recentItems = function () {
Items.find({item_published: true}).exec(function(err,item){
if(!err)
return item
});
};
And I want to use it like this:
var x = recentItems();
But this fails with undefined value due to Async behavior of recentItems. I know that I can change my function to use a callback like this:
recentItems = function (callback) {
Items.find({item_published: true}).exec(function(err,item){
if(!err)
callback(item)
});
};
And:
recentItems(function(result){
var x = result;
});
But i dont want to use this method because i have a situation like this. i have a function that should do two operations and pus result to an array and after them, fire a callback and return value:
var calc = function(callback){
var arr = [];
var b = getValues();
arr.push(b);
recentItems(function(result){
var x = result;
arr.push(x);
});
callback(arr);
};
In this situation, the value of b pushed to arr and the main callback called and after that value of x fetched from recentItems duo to Async behavior of recentItems. But I need this two operation runs sequentially and one after one. After calculating all of them, then last line runs and the callback fired.
How can I resolve this? I read about the Promises and Async libraries, but I don't know which of them is my answer. Can I overcome this with raw Node.js? If so, I would prefer that.
There are some ways of doing what you want, but none of them are ~perfect~ yet.
There is an ES7 proposal of native async/await that will be the callback heaven, but atm, you can do:
Nested callbacks (native, but very ugly and unmaintainable code)
Promises (good, but still too verbose)
Async/Await library (It's an amazing library, but very far from native, and performance isn't cool)
ES7 transpiler - you can write the ES7 code today, and it will transpile for you to ES5 (e.g Babel)
But, if you're already using the newest version of NodeJS (4.0.0 as the time of writing) - and if you're not, you really should - the best way of achieving what you want is to use generators.
Combined with a small library named co, it will help you to achieve almost what the ES7 async/await proposes, and it will mostly use native code, so both readability and performance are really good:
var co = require('co');
var calc = co(function *calc() {
var arr = [];
var b = getValues();
arr.push(b);
var items = yield recentItems();
arr.push(items);
return arr;
});
function recentItems() {
return new Promise(function(resolve) {
Items.find({item_published: true}).exec(function(err, item) {
if(!err)
resolve(item);
});
}
You can read more about this subject in this awesome Thomas Hunter's blog post.
You've almost got it. There is no method to work-around callbacks. However, you can certainly use callbacks to do what you want. Simply nest them:
var calc = function(callback){
var arr = [];
getValues(function(b){
arr.push(b);
recentItems(function(result){
var x = result;
arr.push(x);
callback(arr);
});
});
};
You can try something like this. It still nests the callbacks, but the code is a little cleaner.
var callA = function(callback) {
//Run the first call
prompt(callback(data));
}
var callB = function(callback) {
//Some other call
prompt(callback(data));
}
callA(function(dataA) {
callB(function(dataB) {
//Place a handler function here
console.log(dataA + " " + dataB)
})
});
I'm confused as to why I cannot get this service call to perform as needed. The console.log's within definitionsService.get promise resolution are what I would expect (the object I'm aiming to return). However the console.log right before I return defs is undefined which, of course, means my returned value is undefined. What am I missing?
function getDefinitions() {
var defs;
definitionsService.get().$promise.then(function(data) {
console.log(data);
defs = data;
console.log(defs);
});
console.log(defs);
return defs;
};
I changed the above to:
function getDefinitions() {
var defs = $q.defer();
definitionsService.get().$promise.then(function(data) {
defs.resovle(data);
});
return defs.promise;
};
per the below answer.
I also changed the way I call this method per the same answer like this:
function detail(account) {
getDefinitions().then(function(definitions) {
var key = angular.isDefined(definitions.ABC[account.code]) ? account.code : '-';
return definitions.ABC[key].detail;
});
}
Then in my controller I'm trying to do the following:
var getAccounts = function() {
playersService.getAccounts({
playerId: playerId
}).$promise.then(function(accounts) {
for (var i = 0; i < accounts.length; i++) {
accounts[i].detail = utilitiesService.detail(accounts[i]);
}
vm.accounts = accounts;
});
};
var init = function() {
getAccounts();
};
init();
My problem is that accounts[i].detail is consistently undefined.
Welcome to the world of asynchronous calls.
If you are making an async call inside getDefinitions (from definitonsService), then you must assume getDefinitions() is async as well. Which means you cannot simply return defs.
When you print defs before returning it, the async call of the service has yet to have been carried out.
defs should also be a promise object. You can then return it as you do, but the method invoking it should also use it with .then(function(defs)...
Or in code form:
function getDefinitions() {
var defs = $q.defer();
definitionsService.get().$promise.then(function(data) {
defs.resovle(data);
});
return defs.promise;
};
And whoever calls getDefinitions() :
getDefinitions().then(function(defs) {
// do something with defs...
}
Answer to question after edit:
The problem is once again with the async nature inside the getAccounts method.
utilitiesService.detail is an async method. So you are in fact assigning promises, not values, to accounts[i].detail.
Since each account will entail an async call, using $q.all seems like a good idea:
var promises = [];
for (var i = 0; i < accounts.length; i++) {
promises.push(utilitiesService.detail(accounts[i]));
}
$q.all(promises).then(function(values)){
// you should have all the account details inside 'values' and can update vm.accounts
}
remember - getting the promise is synchronous. Getting the value that the promise... well, promises to get you - that's asynchronous.
The problem is that you are returning defs before the promise has resolved, while defs === None.
You can solve this by changing your code a bit:
function getDefinitions() {
return definitionsService.get().$promise.then(function(data) {
return data;
});
};
I have just started using promises in attempt to cleanup some 'callback hell'.
I've decided on trying bluebird and I am running it in the browser but immediately ran into scoping problems.
Is there a way of setting the thisArg in a new Promise? The below example shows that the 'this' value inside the promise resolver is set to the browser window, but I'd like it set to the surrounding scope so I can easily access member variables.
I noticed there is a .bind() method but it only scopes the 'then()' method, not the promise. I also realize I can have 'var me = this' just before the promise and use closure, but I wanted to avoid it if possible.
function MyObject() {
this.value = 7;
}
MyObject.prototype.getValue = function () {
return new Promise(function (resolve) {
// some request/processing that takes a long time
var result = Ajax.request(...);
resolve({
value: this.value,
result: result
});
// 'this' is set to window instead of the instance,
// resulting in this.value as undefined
});
}
var obj = new MyObject();
obj.getValue().then(function (value) {
console.log(value); // preferably outputs 7
})
No, there is not. You can of course use the default approaches, but you shouldn't need to.
When doing heavy processing and getting back the value asynchronously, you want to get a promise for the value. You don't need to set the result value as a property of the original instance.
MyObject.prototype.getValue = function () {
return new Promise(function(resolve) {
// lots of processing to make a `value`
resolve(value); // no `this` at all!
});
};
In case you want to synchronously get the .value that you had set on the instance, you don't need the Promise constructor. Just use Promise.resolve to make a promise for an existing value:
MyObject.prototype.getValue = function () {
// TODO: lots of processing
return Promise.resolve(this.value);
};
Or, in your case, even Promise.method:
// TODO: lots of processing
MyObject.prototype.getValue = Promise.method(function () {
return this.value;
});
This is more a comment then an answer, as it is primary opinion based.
In the rar situations where I need this it would look like this in my code:
Ajax.requestAsync in this case would be a promisifyed version of Ajax.request.
I know this might just move your problem to another place.
MyObject.prototype.getValue = function () {
return Ajax.requestAsync(...)
.bind(this)
.then(function(result) {
return {
value: this.value,
result: result
}
});
}
Or something like this:
MyObject.prototype.getValue = function () {
var returnValue = {
value: this.value
};
return Ajax.requestAsync(...)
.then(function(result) {
returnValue.result = result;
return returnValue;
});
}
A rarely use such constructs:
MyObject.prototype.getValue = function () {
return Promise.all([this, Ajax.requestAsync(...)])
.spread(function(object, result) {
return {
value: object.value,
result: result
};
});
}
I'm using phonegap with application preferences plugin and trying to make a helper function to get a value from it. However the function is not returning a correct value. I know this has to do with asynchronous thingy but unfortunately I don't know how to fix it. (I've tried to search help here, and found little, and tried to implement it in helper method)
What I want to achieve is:
function populateList() {
var a = 1;
var number = getSettingFromApplicationPreferences('number');
// number is always undefined
var letter = getSettingFromApplicationPreferences('letter');
// letter is always undefined
number = (number) ? number : 1;
letter = (letter) ? letter : 'b';
// Here I'll do some DOM manipulation and use 'number' and 'letter' on it, and
// define new variables based on 'number' and 'letter'
}
here's the helper function that I need help with:
function getSettingFromApplicationPreferences(setting) {
var x = (function () {
window.plugins.applicationPreferences.get(
// setting
setting,
// success callback
function(returnValue) {
console.log(setting + ': ' + returnValue);
return returnValue;
},
// error callback
function(error) {
alert("Failed to get a setting: " + error);
return false;
}
);
})();
return x;
}
Question
How is it possible to return the 'returnValue' from application preferences with that helper function?
The problem is, your callback doesn't actually set a value for x. So, you're going to have some other way to do whatever you're doing, because return values will not work.
You are using an asynchronous function incorrectly, you cannot assign like you are because the function hasn't returned yet and so you get an undefined. You need to use a callback function instead.
That means that inside the success function you would do whatever you need to do with the "returnValue".
I have an Ajax call with a callback. (I am using YUI for this). The basic call looks like this:
function something() {
var callback1_success = function (o) {…
};
var callback1_failure = function (o) {…
};
var callback1 = {
success: callback1_success,
failure: callback1_failure
};
var callback2_success = function (o) {…
};
var callback2_failure = function (o) {…
};
var callback2 = {
success: callback2_success,
failure: callback2_failure
};
var ajax_params = buildAjaxParams(….);
Y_GET(ajax_params, callback1);
var ajax_params = buildAjaxParams(….); // different stuff
Y_GET(ajax_params, callback2);
} // something
function Y_GET(p_parms, p_callback) {
request = YAHOO.util.Connect.asyncRequest('GET', p_parms, p_callback);
return (request);
} // Y_GET
This all works fine.What I want to do is send the callback to the server, the server will not process the callback parameters and will send them back in the result set.
So, Y_GET will become: Function Y_GET(p_parms, p_callback) {
var parms_to_send = p_parms + “ & passthrough = ” + p_callback;
request = YAHOO.util.Connect.asyncRequest('GET', parms_to_send, local_callback);
return (request);
} // Y_GET
var local_callback = {
success: function (o) {
o.responseText.passthrough.success
},
failure: function (o) {
o.responseText.passthrough.failure
}
}; /* paste in your code and press Beautify button */
if ('this_is' == /an_example/) {
do_something();
} else {
var a = b ? (c % d) : e[f];
}
So, how do I pass a callback function to the server; which returns the name, and call it. If there is another approach, I am open to that, of passing a callback function and acting upon it in the response set.
Thank you.
Off the top of my head, there are a couple of approaches I can think of. eval() is a possibility, but is generally considered something to avoid given the risk of running arbitrary JS code (ultimately this depends on who is providing the string which is being evaled).
I would recommend the following approach:
Create you functions as declarations on a basic JS object.
var Callbacks = {
callback1: function(){ },
callback2: function(){ }
};
Then, use the string returned from your AJAX call as a property indexer into your Callbacks object. I'm not familiar with YUI AJAX requests, but hopefully you get the idea:
var p_callback = function(){
var local_callback = // parse response, get the callback method you want by name/string
Callbacks[local_callback](); // providing arguments as needed, of course
};
YAHOO.util.Connect.asyncRequest('GET', p_parms, p_callback);
By using property accessors on your object you are assured that you are executing only your own callback code, instead of arbitrary JavaScript that may have been included in the response.