How to find function caller while in strict mode - javascript

I have a function, getGames(), in my Angular controller which can be called by both my init() function and an update() function. I need to know whether init() or update() called this function because I treat each situation differently.
I tried to access arguments.callee.caller.toString(), but this is not allowed while in strict mode, which is a requirement for this project.
How could I access the caller of getGames() while in strict mode?
My current structure below. Obviously loadingGames.promise within updateSchedule() does not work because that promise was already resolved when init() ran. I'm struggling to refactor this so that init() and updateSchedule() each depend on a different promise resolution with regard to the same function, getGames().
var loadingGames = $q.defer();
var getGames = function() {
playersService.getGames({
playerId: playerId
}).$promise.then(function(data) {
vm.games = data;
loadingGames.resolve();
});
};
var init = function() {
getGames();
}
init();
var updateSchedule = function() {
getGames();
loadingGames.promise.then(function() {
populateOptions(vm.games);
vm.tableParams.reload();
});
};
My thought was to determine the caller at the end of getGames() then resolve a different promise based on who the caller was.

Your getGames()-function could return a promise that is resolved as soon as the games have been fetched from the server(to make my example code shorter I left out the parameter to the service and assumed that it returns a promise):
var games; //This is vm.games in your case
(function fetchGames() {
games = playersService.getGames()
.then(function(data){
games = data;
return data;
});
})();
function getGames() {
return $q.when(games);
}
function updateSchedule() {
getGames()
.then(function(theGames){
populateOptions(theGames);
tableParams.reload();
});
}
$q.when(x) returns a promise that is immediately resolved with x if x is not a promise. If x is a promise, it returns x directly.
Just a note: Your populateOptions and tableParam.reload functions look a lot like you do manual DOM-stuff. This is almost always wrong in angular - let data binding do that job for you.

Related

How to get response data to be used outside function

How do I get response data to be used outside function?
var myVar;
uploadService.getUploadFolderId(folderId).then(function(response){
console.log("Show docs id", response.data); // I get output :)
myVar = response;
return myVar;
}).$promise;
console.log("show", myVar) // output: undefined
I did some reading and practices about global function and function hoisting but I still could not get it to work. Please help and thanks in advance.
You can utilize the Promise API in javascript. Essentially you:
Create your local variable (value below)
Create your Promise
Perform some long running task in your promise, and call resolve when you have what you want
Pass an object into the resolve handler that you want to preserve (expose outside of the `Promise)
Utilize the then(...) handler on the Promise and extract the object you resolved earlier
Example:
var value = 'Foo';
var promise = new Promise(
function(resolve, reject) {
setTimeout(function() {
resolve('Bar')
}, 1000);
});
promise.then(function(val) {
value = val;
console.log(value); // Bar
});

Scoping within Angular service call

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;
});
};

AngularJS : Return a promise which is constructed from the result of another promise in angular?

I have the following function defined in a service, in which I use $http to get some data and set some of the fields of an object:
function getComplexObject () {
var complexObject = {
'A': null,
'B': null
}
$http.get('someURI').then(function(data) {
complexObject.A = data;
//Do some processing and set B
return complexObject;
})
};
So until now I've used a promise with the $http service but I don't know how to wrap the function with $q so that I can ultimately use it in the following manner:
//Using my function as a promise
myService.getComplexObject().then(function (data) {
this.complexObject = data;
})
I've searched for tutorials on angular promises but most of them simply explain what a promise is or do something similar to what I already did, I'm guessing what I want to do is possible but I can't find the right syntax or the right tutorial.
Just put return in front of the $http.get(...).then(...) chain, i.e.
return $http.get(...).then(...);
This will ensure that the result of getComplexObject is a promise that will (when its resolved) contain the value of complexObject.
That is exactly what .then is designed for - it takes the promise from $http.get() and returns a new promise that will ultimately be resolved with its own return value instead of the original one.
FWIW, I'd put the declaration of complexObject inside the .then call - there's no need for it to be in the outer scope and it'll inadvertently cause a closure over that variable.
NB: Also, be careful about using this in your final callback - it may not be what you think it is!
You could return a promise and resolve if later (see: https://docs.angularjs.org/api/ng/service/$q).
function getComplexObject () {
var deferred = $q.defer();
var complexObject = {
'A': null,
'B': null
}
$http.get('someURI').then(function(data) {
complexObject.A = data;
//Do some processing and set B
deferred.resolve(complexObject);
});
return deferred.promise;
};

Bluebird Promise Scope

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
};
});
}

Collect data to object with four async calls and handle the object onready

I have a handler (callback), an object to handle and four functions, which collect the data to object. In my case I wish to asynchronously call four data retrievers and when execution of all four is complete, handle the resulting object (something similar to the following):
var data = {};
function handle (jsObj) {}
// data retrieving
function getColorData () {}
function getSizeData () {}
function getWeightData () {}
function getExtraData () {}
data.color = getColorData();
data.size = getSizeData();
data.weight = getWeightData();
data.extra = getExtraData();
handle( data );
Of course, this code will not work properly. And if I chain data retrieving functions, they will be called one after another, right?
All four functions should be called asynchronously, cause they are being executed for too long to call them one by one.
Updated:
Thanks to everybody for your suggestions! I prefered $.Deferred(), but I found it slightly difficult to make it work the way I need. What I need is to asynchronously make a view, which requires four kinds of data (extraData, colorData, sizeData & weightData) and I have three objects: App, Utils & Tools.
Just a small description: view is created by calling App.getStuff passed App.handleStuff as a callback. Callback in the body of App.getStuff is called only $.when(App.getExtraData(), App.getColorData(), App.getSizeData(), App.getWeightData()). Before that Utils.asyncRequest passed Tools.parseResponse as a callback is called.
So, now the question is should I create four deferred objects inside each App.get*Data() and also return deferred.promise() from each of them?
And should I deferred.resolve() in the last function in my order (Tools.parseResponse for App.getExtraData in my example)?
var view,
App,
Utils = {},
Tools = {};
// Utils
Utils.asyncRequest = function (path, callback) {
var data,
parseResponse = callback;
// do something with 'data'
parseResponse( data );
};
// Tools
Tools.parseResponse = function (data) {
var output = {};
// do something to make 'output' from 'data'
/* So, should the deferred.resolve() be done here? */
deferred.resolve(output);
/// OR deferred.resolve();
/// OR return output;
};
// App
App = {
// Only one method really works in my example
getExtraData : function () {
var deferred = new jQuery.Deferred();
Utils.asyncRequest("/dir/data.txt", Tools.parseResponse);
return deferred.promise();
},
// Others do nothing
getColorData : function () { /* ... */ },
getSizeData : function () { /* ... */ },
getWeightData : function () { /* ... */ }
};
App.getStuff = function (callback) {
$.when(
App.getExtraData(),
App.getColorData(),
App.getSizeData(),
App.getWeightData()
)
.then(function (extraData, colorData, sizeData, weightData) {
var context,
handleStuff = callback;
// do something to make all kinds of data become a single object
handleStuff( context );
});
};
App.handleStuff = function (stuff) { /* ... */ };
/// RUN
view = App.getStuff( App.handleStuff );
I did not expect the code in my example above to work, it is for illustrative purposes.
I've been trying to solve this for quiet a long time and it still gives no result. The documentation for jQuery.Deferred() and discussions around this, unfortunately, did not help me. So, I would be very glad and greatful for any help or advise.
Conceptually, you would use a counter that gets incremented as each asynchronous call completes. The main caller should proceed after the counter has been incremented by all the asynchronous calls.
I think what you're looking for are Promises / Deferreds.
With promises you can write something like:
when(getColorData(), getSizeData(), getWeightData(), getExtraData()).then(
function (colorData, sizeData, weightData, extraData) {
handle(/*..*/);
}
)
The get*Data() functions will return a promise that they fulfill when their assynchronous call is complete.
Ex:
function getData() {
var promise = new Promise();
doAjax("getData", { "foo": "bar" }, function (result) {
promise.resolve(result);
});
return promise;
}
The when simply counts the number arguments, if all it's promises are resolved, it will call then with the results from the promises.
jQuery has an OK implementation: http://api.jquery.com/jQuery.when/
What I could suggest for this scenario would be something like that.
write a function like this
var completed = 0;
checkHandler = function() {
if(completed == 4) {
handle(data);
}
}
where completed is the number of positive callbacks you must receive.
As soon as every function receives a callback you can increment the "completed" counter and invoke the checkHandler function. and you're done!
in example
function getColorData() {
$.get('ajax/test.html', function(data) {
completed++;
checkHandler();
});
}

Categories