views:

856

answers:

5

I want to turn an asynchronous function to the synchronous.

function fetch() {
  var result = 'snap!';
  $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function messyCallback(data){
    result = data;
  });
  return result;
}

document.write(fetch());​

See in action

The result always will be 'snap!', because $.getJSON run after fetch() is done.

My first idea was:

function fetch() {
  var result = 'snap!';
  $.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function messyCallback(data){
    result = data;
  });
  while (true) {
    if (result != 'snap!') return result;
  }
}

It doesn't work and also blow off the browser.

I read about generators and iterators in JS 1.7, but I have no idea how to apply it to my problem.

This question is not really about jQuery. Instead of $.getJSON could be any another asynchronous function.

+5  A: 

See this question also: http://stackoverflow.com/questions/2097367/halt-javascript-execution-without-locking-up-the-browser

Doing exactly what you want doesn't work. You can't create synchronousness from asynchronousness (only the other way around!) in a single-threaded event-driven environment. You should embrace the async nature of the language, and develop your own consistent style around handling callbacks so that your code is readable/maintainable. You could, for instance, choose to never do any real work inside a callback closure, but simply call another top-level function/method for clarity.

quixoto
+1  A: 

The lower-level $.ajax() function from jQuery has more options, including async: [true|false] (default is true).

Nevertheless, in most cases you should follow Ben's advice and "embrace the async nature of the language".

Drew Wills
AFAIK, these options do not work with JSONP calls.
NV
Drew: Thanks for calling this out. It's true that JS has a small number of (confusingly) synchronous calls, XMLHttpRequest's `get` being one of them.
quixoto
I meant `async:false` doesn't work with JSONP calls.
NV
+1  A: 

You want to roll your own $.getSyncJSON that uses $.ajax({async:false}). See: http://api.jquery.com/jQuery.ajax/.

I must advise you against this course of action, however. You can easily lock up the browser from all input, putting your UI at the mercy of the network. But if you know what you are doing and are sure of your use case, go for it.

Plynx
+1  A: 

Instead of writing helper methods like your fetch that return a value, make them accept another function, a "receiver", to pass their result to:

function fetch(receiver) {

    $.getJSON("blah...", function(data) {

        receiver(data);
    });
}

Obviously this is redundant because it's exactly how getJSON already works, but in a more realistic example the fetch function would process or filter the result somehow before passing it on.

Then instead of:

document.write(fetch());​

You'd do:

fetch(function(result) { document.write(result); });

Generators can be used to make asynchronous code a lot more linear in style. Each time you needed some asynchronous result, you'd yield a function to launch it, and the generator would resume when the result was available. There'd be a bit of management code keeping the generator going. But this isn't much help because generators are not standard across browsers.

If you're interested, here's a blog post about using generators to tidy up asynchronous code.

Daniel Earwicker
In your code receiver should be inside callback function.
NV
@NV - oops, good call.
Daniel Earwicker
I had a play with Mozilla generators and they seem excellent. Shame they aren't widely adopted elsewhere. See the link I've added to the end of the answer.
Daniel Earwicker
A: 

There is an extension to the JavaScript language called StratifiedJS. It runs in every browser, and it allows you to do just that: handling asynchronous problems in a synchronous/linear way without freezing your browser.

You can enable Stratified JavaScript e.g. by including Oni Apollo in your webpage like:

<script src="http://code.onilabs.com/latest/oni-apollo.js"&gt;&lt;/script&gt;
<script type="text/sjs"> your StratifiedJS code here </script>

And your code would look like:

function fetch() {  
  return require("http").jsonp(
    "http://api.flickr.com/services/feeds/photos_public.gne?" +
    "tags=cat&tagmode=any&format=json", {cbfield:"jsoncallback"});
}
document.write(fetch());​

Or if you really want to use jQuery in StratifiedJS:

require("jquery-binding").install();
function fetch() {  
  var url = "http://api.flickr.com/?format=json&amp;...&amp;jsoncallback=?"
  return $.$getJSON(url);
}
document.write(fetch());​

The docs are on http://onilabs.com/docs

tomg