3

Is there a way to perform a synchronous AJAX query that doesn't freeze the browser? In my opinion synchronous requests are a lot easier to work with in most cases, but the fact that they block other parts of the code from executing is a real killer. Is there a way to get synchronous AJAX without the negative side effects? (And yes, I realize that the term "Synchronous AJAX" is an oxymoron.)

Ajedi32
  • 45,670
  • 22
  • 127
  • 172
  • 4
    I don't think so... I'd suggest you learn about promises, the make working with AJAX easier. – elclanrs Jul 22 '13 at 00:02
  • @hexacyanide Sorry, I guess that was poorly worded. I mean that it doesn't freeze the browser or block other events in my code from triggering. – Ajedi32 Jul 22 '13 at 00:03
  • Even if "non-blocking synchronous" wasn't a contradiction, it is impossible for such a construct in a single-execution (aka single-threaded) environment. The alternative, and indeed solution in browser JavaScript, is "non-blocking *asynchronous*". – user2246674 Jul 22 '13 at 00:13
  • Related: http://stackoverflow.com/q/5832276/1157054 – Ajedi32 Jul 22 '13 at 00:15
  • Also potentially helpful here: https://github.com/tj/co and https://github.com/lukehoban/ecmascript-asyncawait – Ajedi32 Sep 11 '15 at 15:40
  • Non-blocking == asynchronous – jpaugh Oct 30 '17 at 23:06

5 Answers5

5

I'll provide an example of the bad side of effects of allowing such behavior.

Lets say you have this program:

<script>
var file = "foo.json";

function nullIt() {
    file = null;
}

function loadFile() {
    if (file != null) {
        synchronousLoad(file);//imagine the load takes 5 seconds
        alert("i just loaded: " + file);
    }
}

window.onload = loadFile;
</script>
<button onclick="nullIt()">click me</button>

The bad thing here-

  • while the synchronousLoad() is blocking for 5 seconds, the user clicks the button, and the event handler quickly runs to completion.
  • Now the file variable is null.
  • synchronousLoad() finishes and returns, letting execution resume on the next line of code
  • but file is now null, and the message output to the user is broken.

The real issue here you cannot reason about your code the same way anymore. Just because some fact was true on line 5, doesnt mean its still true on the very next line. This makes it very difficult to write an error free program.

Some programming languages support multithreading, and you have to deal with these issues, although you have tools to help deal with these problems. But, it's still a lot of extra effort on the programmers part.

Comparatively speaking, using callbacks to do asynchronous operations is ez-mode.

goat
  • 31,486
  • 7
  • 73
  • 96
  • 1
    Ah, excellent point. Personally I'd prefer to take my chances with those sort of "race conditions" (if you can call it that), but it's nice to see some justification for why things are the way they are. – Ajedi32 Jul 22 '13 at 13:47
4

No. Synchronous is, by definition, blocking. Nothing can proceed until the process completes. That includes the rest of the UI in the web browser.

It's supposed to be asynchronous, so the best approach is to design the code to work asynchronously.

David
  • 208,112
  • 36
  • 198
  • 279
2

In the upcoming ECMAScript 2016 (ES7) standard, there is a new set of language keywords designed to do something very similar to what you seem to be looking for, called async and await.

These keywords don't allow "non-blocking synchronous AJAX", but they do allow you to write asynchronous code in a way that looks synchronous. Here's a quick example:

// Let's say you have an asynchronous function that you want to call in a synchronous
// style...
function delayedEval(delay, func) {
  // First, ensure that the function returns a Promise object. If it doesn't, wrap it with
  // another function that does.
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(func()), delay)
  })
  // For more on Promises, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
}

// Then, declare a function as asynchronous. This causes it to return a Promise that 
// resolves to its return value, instead of returning its return value directly.
async function delayedHello() {
  // Inside an async function, you can call other async functions like this, which looks
  // very much like a synchronous call (even though it isn't).
  let message = await delayedEval(1500, () => "Hello, world!")
  console.log(message)
}

// This returns (a Promise) immediately, but doesn't print "Hello, world!" until 1.5
// seconds later. (At which point it resolves the Promise.)
delayedHello()

Try in Babel

Basically, instead of "synchronous AJAX without the negative side effects", async and await get you asynchronous AJAX without all its negative side effects. (Messy code with lots of logic for handling callbacks.)

async and await are part of the "async functions" candidate recommendation in the ES7 standard.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Ajedi32
  • 45,670
  • 22
  • 127
  • 172
1

I don't think it is possible. Instead of trying to use synchronous queries, you can use the asynchronous approach and use the AJAX done event.

Example using jQuery.ajax()

jQuery.ajax({
    url: "URL HERE",
    data: "whatever you are sending"
}).done(function (data) {
    // Do things with data
});

So, instead of using a synchronous request, you can use the Asynchronous request and just execute code AFTER the request has completed. So, it won't freeze your browser.

rgoomar
  • 184
  • 1
  • 3
  • Yeah, but this is exactly what I'm trying to avoid by doing a synchronous request. There's nothing in my code's current path of execution that needs to be executed until after the request is complete so it seems cleaner to just do a synchronous request. I just don't want it to block other parts of my code while it's waiting. – Ajedi32 Jul 22 '13 at 00:14
  • @Ajedi32 It's not cleaner at all - in particular because it *doesn't work* with a single execution context, such as that employed by browser JavaScript. In addition, when multiple execution contexts are used (i.e. threads or even continuations) then there are additional considerations and issues to deal with: implicit atomic execution is removed and [more forms of] race conditions must be guarded against. – user2246674 Jul 22 '13 at 00:18
  • 1
    @Ajedi32 Using promises (in particular, `then` as implemented in jQuery 1.8+ and codified by [Promises/A](http://wiki.commonjs.org/wiki/Promises/A)) can greatly clean up asynchronous code usage. I find the promises model much more consistent and clean to deal with than the previous success/error callbacks. – user2246674 Jul 22 '13 at 00:22
0

For the reasons outlined in the other answers here, there is no way to perform a synchronous AJAX call without blocking other events in the browser.

However, if code complexity is your primary reason for wanting to avoid asynchronous method calls, you may be interested in the Javascript cross-compiler streamline.js, which allows you to write asynchronous method calls as if they were synchronous, and get the same result as if you wrote the call asynchronously.

From the project's GitHub page:

streamline.js is a language tool to simplify asynchronous Javascript programming.

Instead of writing hairy code like:

function archiveOrders(date, cb) {
  db.connect(function(err, conn) {
    if (err) return cb(err);
    conn.query("select * from orders where date < ?", [date], function(err, orders) {
      if (err) return cb(err);
      helper.each(orders, function(order, next) {
        conn.execute("insert into archivedOrders ...", [order.id, ...], function(err) {
          if (err) return cb(err);
          conn.execute("delete from orders where id=?", [order.id], function(err) {
            if (err) return cb(err);
            next();
          });
        });
      }, function() {
        console.log("orders have been archived");
        cb();
      });
    });
  });
}

you write:

function archiveOrders(date, _) {
  var conn = db.connect(_);
  conn.query("select * from orders where date < ?", [date], _).forEach_(_, function(_, order) {
    conn.execute("insert into archivedOrders ...", [order.id, ...], _);
    conn.execute("delete from orders where id=?", [order.id], _);
  });
  console.log("orders have been archived");
}

and streamline transforms the code and takes care of the callbacks!

No control flow APIs to learn! You just have to follow a simple rule:

Replace all callbacks by an underscore and write your code as if all functions were synchronous.

For more information on streamline.js, read the blog post Asynchronous Javascript – the tale of Harry.

Ajedi32
  • 45,670
  • 22
  • 127
  • 172