10

This is an odd problem. I have a client object that I am building up using Crockford-esque public/private members:

var client = function() {
  var that, remote_data, other_data; 

  // add public interface
  that.doStuff = function(){...}

  // wait for remote resources to load
  remote_data = jsonRequest1();
  other_data  = jsonRequest2();

  return that;
};

The problem I'm having is that I need to load some remote JSON resources prior to returning the new 'that' object (which signals a ready client). Data is returned asynchronously (obviously), and I am setting boolean variables to indicate when each remote resource has returned.

I've thought about doing something like the following:

return whenInitialized(function() { return that; });

The whenInitialized function returns whether or not both of the boolean flags are true. I'd use this with a combination of setInterval, but I am sure this won't work.

Would appreciate your suggestions.

findchris
  • 1,704
  • 4
  • 14
  • 13

2 Answers2

17

In order to run code after an asynchronous operation has succeeded, you need a continuation. It can be just a callback that your code calls when the operations are complete.

Something like this:

var client = function(done) { // done is the callback
  var that, remote_data, other_data; 

  // add public interface
  that.doStuff = function(){...}

  // wait for remote resources to load
  var done1 = false, done2 = false;
  var complete1 = function() { done1 = true; if (done2) done(); };
  var complete2 = function() { done2 = true; if (done1) done(); };
  remote_data = jsonRequest1(complete1);
  other_data  = jsonRequest2(complete2);

  return that;
};

But these controlling flags are really annoying and don't really scale. A better, declarative way of doing this is using something like jQuery deferreds:

$.when(jsonRequest1(), jsonRequest2()).then(done);
Jordão
  • 55,340
  • 13
  • 112
  • 144
  • 1
    +1 for jQuery deferreds. I'm just starting to get the hang of them, and they do such wonderful things. – zzzzBov Dec 01 '11 at 03:44
  • Thanks Jordão. I understand using the callback pattern as you've suggested, calling the 'done' callback when data has all been loaded. Example usage: var my_client = client(function() { alert('done!'); }); However, the my_client variable would be usable, in an invalid state, while waiting for the callback. How would you reconcile this? I guess one solution is to change the client interface so a returned client isn't expected. – findchris Dec 01 '11 at 19:23
  • @Chris Johnson: yes, you could provide that client to the callback: `done(that)`, and don't return anything from the `client` function. Then your client code is like: `client(function(my_client) { ... });`. – Jordão Dec 01 '11 at 19:29
1

You can do a loop (optionally with a timeout) to wait for the async to finish. Warning, this will (as requested) block all other functionality and may cause the browser to freeze if it takes much too long. However, you really should figure out an asynchronous way to do what you need instead of blocking like this.

var syncRequest = function(options) {
    var is_finished = false;
    var result;
    var finished_callback = function(response) {
        is_finished = true;
        result = response.result;
    }

    ajax_request(options, finished_callback);

    // timeout in 30 seconds
    var timeout = (new Date()).getTime() + 30000;
    while ( !is_finished ) {
        if ( (new Date()).getTime() >= timeout ) {
            alert('Request timed out');
        }

        // do nothing, just block and wait for the request to finish
    }

    return result;
}
six8
  • 2,886
  • 21
  • 20
  • 1
    I've performed a similar experiment before but I always got my browser frozen. Since most browsers execute JavaScript in only one thread, it's not a good idea to use a polling (as JavaScript doesn't have `sleep()` or anything like that). – Ryan Li Dec 01 '11 at 03:42
  • 1
    Indeed. It's a terrible solution, but a valid one for what the OP asked. It is really best to figure out an async way to do it and I believe all problems have an async solution if you think about it enough. – six8 Dec 01 '11 at 03:45
  • If you're going to use a blocking solution why not just do a synchronous ajax request in the first place? – nnnnnn Dec 01 '11 at 04:12
  • The OP asked for a blocking solution. If you're doing JSONP you can't do a synchronous request. – six8 Dec 01 '11 at 04:54