views:

137

answers:

5

This question in summary is to figure out how to pass variables between javascript functions without: returning variables, passing parameters between primary functions, using global variables, and forcing function 1 to wait for function 2 to finish. I figured out a jQuery solution and posted in below (in the answers section).


Old Post: I initialize a set of four functions, each calling on each other in a different way. At the end of it, I need the final modified product (an array) returned to the initializing function.

Global variables don't force the initial function to wait. And returning it backwards four times doesn't work either. How do you pass a modified variable back to its initializing function, if you can't return it? Or why isn't it returning?

(the maze starts at initFunctionA, ends at functionD)

classOne = {
  initFunctionA : function() {
    classTwo.functionB(functionD, array);
    // I NEED ACCESS TO ARRAY2 HERE
  },
  functionD : function(data, array) {
    var array2 = // modifications to array
  }
}

{...}

classTwo = {
  functionB : function(callback, array) {
    $.ajax({
      success: function(ret){
        classTwo.functionC(ret, callback, array)
      }
    });
  },
  functionC : function(ret, callback, array) {
     callback(ret.data.x, array);
  }
}
+1  A: 

You can't make it work with a pattern like you've written there; it's simply not possible in Javascript because there's no such thing as "waiting". Your ajax code has to take a callback parameter (which you've got, though it's not clear where it comes from or what it does), and that initial function should pass in code to do what you need with the array after the ajax call finishes.

Pointy
@Pointy, I think I figured out a way to mimic "waiting" in javascript regardless of the ajax call. It worked for my purposed but I'd appreciate your opinion. I posted the answer in this answer section.
Emile
+1  A: 

I would use an object constructor:

function ClassOne() {
    this.array2 = [];
}

ClassOne.prototype.initFunctionA = function() {
    // ...
}

ClassOne.prototype.functionD = function(data, array) {
    // Can use array2 EX: this.array2
}

var classOne = new ClassOne();
ChaosPandion
I believe that the crux of the problem is the asynchronous ajax call in the middle of things; it doesn't really have anything to do with the way the classes are built (though it's kind-of hard to tell, I admit).
Pointy
@Pointy, I see why you were confused. I had my alphabet crossed :) I fixed the letters. @ChaosPandion ... I'll come back to this tomorrow and try your solution.
Emile
+1 I never got to test this out, because I needed to keep my classes static and organized in the way the rest of the project is. It might work or it may suffer the problems that Pointy brought up. I'm not expert enough to know. I solved my problem in another way (which I posted below)...but +1 cuz this may be a completely valid solution and it looks like it may do exactly what you say.
Emile
A: 

This is how I understand your problem: classTwo handles an AJAX call and may modify the result. classOne makes use of classTwo to get some data and needs the resulting data.

If so, how's this:

classOne = {
  initFunctionA : function() {
    var array = ['a','b','c'];
    classTwo.functionB(this.functionD, array);
  },
  functionD : function(data, array) {
    // This function is called when the AJAX returns.
    var array2 = // modifications to array
  }
}

{...}

classTwo = {
  functionB : function(callback, array) {
    $.ajax({
      success: function(ret){
        classTwo.functionC(ret, callback, array)
      }
    });
  },
  functionC : function(ret, callback, array) {
     callback(ret.data.x, array);
  }
}

So classOne.initFunctionA calls classTwo.functionB which sets up an ajax call. When the ajax call completes successfully, classTwo.functionC is called with the result and the initial array. From here, classOne.functionD is called with ret.data.x and the array.

Matt
excellent understanding of the function order! I'm not sure I see the solution. I need to use array2 at the end of initFunctionA
Emile
You're not going to be able to do that. The solution is to move all the code from initFunctionA that relies on array2 into functionD. It will behave like the function has been split in two pieces with a delay in the middle (caused by the ajax).
Matt
True that @Matt. Though I just found a solution that does it. Posted below.
Emile
A: 

Okay! I found a way to pass variables between functions without:

  • making global variables
  • making object properties (Chaos's solution)
  • passing parameters

These three were suggested here as the only ways.

http://stackoverflow.com/questions/407048/accessing-variables-from-other-functions-without-using-global-variables

But, if you you can't pass parameters directly, and you need one function to wait for the other (i.e, can't rely on references), and you're using asynchronous calls to the server in an intermediate function, then your only solution is:

Using jQuery...

Create this object in the DOM (dynamically if you don't want to muddy your markup):

<div id='#domJSHandler" style="display: none;"></div>

Then in the function that must wait:

//Function & Class Set 2
$('#domJSHandler').bind('proceedWithAction', function(event, param1, param2) {
    // action set 2
});

And in the function to be waited on:

//Function & Class Set 1
// action set 1
$('#domJSHandler').triggerHandler('proceedWithAction', [param1, param2]);

Essentially encase the last actions you need to perform in a jQuery bind custom event on an invisible DOM object. Trigger that event from JS with jQuery's triggerHandler. Pass your parameters and voila!

I'm sure SO will give me crap for this (and for narcissistically accepting my own answer) but I think it's pretty brilliant for a uber-newbie and it worked for me.

So :p Stack Overflow (jk You've all saved my ass many times and I love you all :)

Emile
Dealing with the DOM is dealing with global state. I think this is what "making global variables" meant, not just using JavaScript globals (i.e. properties on `window` in a browser).
strager
The limitation to global variables as a solution to controlling function order (for instance, when ajax is used), is that the 2nd (3rd, 4th) function carries out its operations on a variable, while the 1st function continues. But I agree with you conceptually. It's just that globals didn't solve my asynchronous problem. This does. (although I just saw your post, let me check it out)
Emile
also, and .unbind() before .bind(...) if user is able to return the that page again.
Emile
This really is not any different than declaring a global function and calling it from the ajax completion handler. Truly, no different at all: the DOM itself is global, so a function bound as an event handler is global by implication. You achieve a level of indirection, as the triggering environment need not know what function will actually run, but you'd get exactly the same effect by having a global variable to refer to the actual function. Of course invoking a global function directly is faster than a custom event, too.
Pointy
if I use global variables, my first function runs without "waiting." Firing an event from ajax's success means that the function called by the ajax won't fire till it's through, but that called function isn't the one I need to wait. I appreciate your insight but the two methods have produced very different results for me.
Emile
+1  A: 

Change your callback (at the call site) such that you capture the return value of functionD. Then, change functionD so that it returns array2. I've added this access to the example below as a convenience. (Also, be sure to include semicolons where "required" if you want to make JSLint happy.)

classOne = {
  initFunctionA : function() {
    var self = this;

    classTwo.functionB(function() {
        var array2 = functionD.apply(self, arguments);

        // ACCESS ARRAY2 HERE
    }, array);
  },
  functionD : function(data, array) {
    var array2 = // modifications to array

    return array2;
  }
};

{...}

classTwo = {
  functionB : function(callback, array) {
    $.ajax({
      success: function(ret){
        classTwo.functionC(ret, callback, array)
      }
    });
  },
  functionC : function(ret, callback, array) {
     callback(ret.data.x, array);
  }
};
strager
@strager - i tried to implement this solution to the letter, but I'm not getting any data back. Although I'm not throwing any errors either, which is bizarre. I'd look into it further but I already found a solution that works (the one I posted). That being said, you seem to know what you're talking about. I'm an amateur and I don't want to mess up the stack overflow MO, so just give me a "yea, I'm confident" and I'll accept this as the answer. Check out my rebuttal to your comment too, if you have the time. Thanks!
Emile
@Emile, Can you post all your code? Are you sure you're returning `array2` in `functionD`? Are you sure you're writing your code which accesses `array2` in the anonymous function in `initFunctionA` and not outside that anonymous function?
strager
I did try it and then I reversed my changes. Forgive me for not posting the code (I'm on a deadline and have to go with my hacky solution). BUT...I learned a lot from your approach. I passed array2 along with several other parameters and I may have not understood the nuances of going with more than just one. That being said, +1 and check. I think this will help someone else for sure.
Emile