Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous programming in JavaScript without messy callbacks

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.

like image 363
NVI Avatar asked Feb 08 '10 15:02

NVI


3 Answers

See this question also: 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.

like image 59
Ben Zotto Avatar answered Oct 03 '22 20:10

Ben Zotto


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.

like image 29
Plynx Avatar answered Oct 03 '22 22:10

Plynx


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.

like image 40
Daniel Earwicker Avatar answered Oct 03 '22 20:10

Daniel Earwicker