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.

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.


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.


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.


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"></script>
<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&...&jsoncallback=?"
  return $.$getJSON(url);
}
document.write(fetch());?

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


The TameJS library is designed to deal with this problem.

You might write something like (untested):

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

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".


I know this a little late but you can avoid callbacks with promises and await in async function

deliverResult = (options) => (
  new Promise( (resolve, reject) => {
    $.ajax(options).done(resolve).fail(reject);
  })
)

getResult = async () => {
  let options = {
    type: 'get', 
    url: 'http://yourUrl.com', 
    data: {param1: 'arg1'}
  }
  console.log('waiting ..... ');
  let result = await deliverResult(options);
  console.log('**..waiting ended..**');
  console.log(result);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type='button' onclick='getResult()' value='click me'/>