Recursive Asynchronous Callbacks in Javascript - javascript

In relation to this question, I'm trying to add a callback to get the data back. So I tried this:
var subgroupIds = [];
var that = this;
this.getSubGroups = function (groupId,callback) {
var anotherObject = this;
this.getGroups("groupId="+groupId, function(groups) {
if ($.isEmptyObject(groups)) {
return;
} else {
$.each(groups, function(index,group) {
subgroupIds.push(group.id);
that.getSubGroups(group.id);
});
anotherObject.callback(group.id);
}
});
}
I thought I have a better understanding of closure after the previous question but I guess I don't...I'm getting the following error:
Uncaught TypeError: Object [object Window] has no method 'callback'
What am I doing wrong here?
Edit
Here's the content of getGroups:
this.getGroups = function(filter,callback,error_callback) {
this.getJSON('/'+apiVersion+'/groups/',function(data){
// run through the filter engine
output = runFilter(data, filter);
callback(output);
},error_callback);
}

It doesn't need to be anotherObject.callback(group.id);, what you need is callback(group.id);
It looks like you're confusing this with arguments object.
arguments holds all parameters that are passed into the function:
var aFunction = function () {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
};
aFunction(1, 2, 3, 4); // 1, 2, 3, 4
While this basically refers to the "owner" of the function (which is, roughly speaking, whatever happens to be before the dot):
var aFunction = function () {
console.log(this);
};
var object1 = { f: aFunction, name: "object1" };
var object2 = { f: aFunction, name: "object2" };
object1.f(); // Object { name="object1", f=function()}
object2.f(); // Object { name="object2", f=function()}
aFunction(); // Window

The callback is a parameter, it is not bound to the context.
I think what you want is to call the callback with anotherObject as the this value, right ?
You can achieve that with :
$.proxy(callback, anotherObject)(group.id);
Or if you only want to execute the callback, and you want to use closure, you need to add :
this.callback = callback; //before
var anotherObject = this;

Related

Javascript - The context of apply

var MyModules = (function Manager() {
var modules = {};
function define(name, deps, impl) {
for (var i=0; i<deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply( impl, deps );
}
function get(name) {
return modules[name];
}
return {
define: define,
get: get
};
})();
MyModules.define( "bar", [], function(){
function hello(who) {
return "Let me introduce: " + who;
}
return {
hello: hello
};
} );
These are the snippets extracted from kyle Simpson's "You don't know JavaScript". However what confuses me is the usage of APPLY. Normally, when we set the context for apply, that is, THIS, the this or context is usually an object or array. But Kyle set it via the passed-in function and I have never seen it before and I am just wondering how it works. The snippet I am describing is this one: modules[name] = impl.apply( impl, deps );. Then I created the simplest snippet myself
function func(par) {
console.log(par);
}
func.apply(func, [1, 2, 3]); // 1
func.apply(null, [1,2,3]); // 1
It does not make any difference whether I pass in func or null as the first parameter for apply. I still get a value 1 displayed in console.

Accessing properties in nested function returning another function

Is there a way to access properties in nested functions like that :
function func(){
this.new_func=function(){
console.log("something");
return 'something';
}
this.someValue=7;
return function(){
return "something_diff";
};
}
var obj=new func();
obj(); //works with returning "something diff"
obj.new_func(); // TypeError: obj.new_func is not a function
obj.someValue; // undefined
I need to delete whole "return function()..." part in order to access "someValue" and "new_func()". Why is it acting like that, and is there a way to somehow access that properties, while still returning another function ??
When you have a constructor that returns an object, that object replaces whatever you assigned to this. So indeed, the members new_func and someValue are lost.
To combine the returned function together with the other members, you can do this:
function func() {
var f = function() {
return "something_diff";
};
f.new_func = function() {
console.log("something");
return 'something';
}
f.someValue = 7;
return f;
}
var obj = new func();
console.log(obj());
obj.new_func();
console.log('someValue:', obj.someValue);
You can do it like this:
var parentFunction = function() {
var nestedFunction = function() {
var value = "nestedValue";
var moreValues = "more values";
return {
value: value,
moreValues: moreValues
}
}
var anotherNestedFunction = function() {
var anotherValue = "nestedValue";
return anotherValue;
}
return {
nested: nestedFunction,
another: anotherNestedFunction
}
}
Then:
var newFunction = new parentFunction();
var nested = newFunction.nested();
console.log("nested value: ", nested.value);
console.log("another nested value: ", newFunction.another);
Here is a working example:
Why is it acting like that, and is there a way to somehow access that properties, while still returning another function ??
Because of the pharenteis:
var obj=new func();
Basically you're firing your function and what is stored the variable obj is what the "func" returns.
In order to access to private properties, you should look at the Closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

In JavaScript, is there a way to retrieve the object a function was bound to?

Specifically, I'm interested in how to get the code below to work, logging as per the comments.
function someWeirdness(func) {
var funcThis = undefined; // Instead of undefined, access to what,
// if anything, func is bound to
// (func.this doesn't work, but that would be
// it if it did).
console.log(funcThis);
}
function greet(greeting) {
console.log([greeting || "Hello", this.name].join(" "));
}
var obj = {
name: "object"
};
greetObj = greet.bind(obj);
someWeirdness(greetObj); // logs obj
The application would be something more along the lines of:
function compositionalMagic(func, params) {
params = Array.isArray(params) ?
params : Array.prototype.slice.call(arguments, 1);
// Additional processing and hooplah
func.apply(func.this, params);
}
this is only available inside of a given function scope — unless you intentionally leak the reference.
function foo( arg ) {
arg.this = this;
}
var obj = { foo: 'bar' };
var obj2 = {};
foo.call( obj, obj2 );
console.log( obj2.this ); // logs { foo: 'bar' }
You could monkeypatch Function.prototype.bind to do something like the above every time you invoke bind, but probably not ideal.

Value not closed over in a loop, despite function-wrapping

I have a list of objects I am looping over; each of these objects has a property on it which is a function referring to this. If I create a list of callbacks based on my objects, it seems that I have to "double-wrap" the this-dependent function in order to maintain the correct references. I don't understand why this is - can anyone explain?
function Pokemon(name) {
this.name = name;
this.sayName = function() {
console.log(this.name);
};
}
function runFunctions(arrayOfFunctions) {
for (var i = 0, fn; fn = arrayOfFunctions[i]; i++) {
fn();
}
}
var monsters = [
new Pokemon('Charmander'),
new Pokemon('Squirtle'),
new Pokemon('Bulbasaur')
];
var unclosedCalls = [];
var closedCalls = [];
for (var i = 0, monster; monster = monsters[i]; i++) {
var unclosedCall = (function(m) {
return m.sayName
})(monster);
var closedCall = (function(m) {
return function() {
m.sayName();
}
})(monster);
unclosedCalls.push(unclosedCall);
closedCalls.push(closedCall);
}
console.log('--start')
runFunctions(unclosedCalls); // doesn't work
console.log('----')
runFunctions(closedCalls); // works
console.log('--end')
closedCalls is the list of double-wrapped callbacks.
I don't get why m in each creation of unclosedCall is not actually closed over.
Here is a jsbin with my code: http://jsbin.com/qivilusuje/1/edit?js,console,output
The problem with the "unclosed" calls is that the function reference that you return (m.sayName) is immediately disassociated from the variable m from which the function property was retrieved.
A function reference doesn't know anything about which object it was retrieved from so therefore when the function is eventually invoked it has no "context" - this will be set to the global object instead of the object that originally had this function as a property:
var obj = {
func : function() { console.log(this) }
}
obj.func() // outputs "obj" because a.b() calls "b" with "this" === "a"
var ref = obj.func;
ref(); // outputs "window"
To fix it you can have the unclosed call do return m.sayName.bind(m), although having got that far there's no need for the IIFE either, and it would work just as well to say:
var unclosedCall = monster.sayName.bind(monster);

Javascript callbacks and "this" [duplicate]

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I have the following Javascript code, and I'm trying to get a callback to work as shown below. I want to see an alert with "123" in it.
var A = function(arg){
this.storedArg = arg;
this.callback = function(){ alert(this.storedArg); }
}
var B = function() {
this.doCallback = function(callback){ callback(); }
}
var pubCallback = function(){ alert('Public callback') };
var a = new A(123);
var b = new B();
b.doCallback(pubCallback); // works as expected
b.doCallback(a.callback); // want 123, get undefined
I understand what is happening but I'm not sure how to fix it. How can I get a callback function that references my a object? In my case, I can make changes to A but not B.
So what you want is to pass the context to the doCallBack.
E.g.
doCallBack = function (callback, callee) {
callback.apply(callee);
}
So then you would do:
b.doCallBack(a.callback, a);
If you cannot modify the B then you can use closure inside A:
var A = function (arg) {
var self = this;
this.storedArg = arg;
this.callback = function () { alert(self.storedArg); }
}
You can create a variable that holds the wanted scope for this by putting it into variable that
var A = function(arg){
this.storedArg = arg;
var that = this; // Add this!
this.callback = function(){ alert(that.storedArg); }
}
Working demo here: http://jsfiddle.net/vdM5t/
I understand what is happening (during the 2nd callback, "this" is b and not a)
No, JS is no class-based language where something could happen. If function(){ alert(this.storedArg); is just called as callback(); (like in b.doCallback), the this keyword points to the global object (window).
To get around that, you'd have to change A to
var A = function(arg){
var that = this; // store reference to the current A object
this.storedArg = arg;
this.callback = function(){
alert(that.storedArg); // and use that reference now instead of "this"
};
}
If you don't expect the storedArg property to change, you could even make it more simple:
var A = function(arg){
this.storedArg = arg;
this.callback = function(){
alert(arg); // just use the argument of the A function,
// which is still in the variable scope
};
}
You need to pass the context you want the callback to execute in:
var B = function() {
this.doCallback = function(callback, context) {
callback.apply(context);
};
};
b.doCallback(a.callback, a); // 123
http://jsfiddle.net/a9N66/
Because inside A.callback function, this does not refer to A but to window object.
var A = function(arg){
this.storedArg = arg;
this.callback = function(){ alert(this.storedArg); }
-----------------------------------^-----------------
}
You can try this,
var A = function(arg){
this.storedArg = arg;
var that = this;
this.callback = function(){ alert(that.storedArg); }
}
var B = function() {
this.doCallback = function(callback){ callback(); }
}
var pubCallback = function(){ alert('Public callback') };
var a = new A(123);
var b = new B();
b.doCallback(pubCallback); // works as expected
b.doCallback(a.callback); // alerts 123
When you do this:
b.doCallback(a.callback);
that just calls a's callback function without telling it to use a for this; so the global object is used for this.
One solution is to wrap that callback up:
b.doCallback(function() { a.callback(); });
Other solutions include binding the callback to a, using jQuery.proxy() (which is just a fancy way of doing my first solution), or passing in a to doCallback and invoking callback on a using apply.

Categories