1

I have this function which pings my server for a specific change in data:

function connected() {
  $.ajax({
    success : function(d) {
      if (d.success) {
        // return data
      } else {
        setTimeout(connected, 200);
      }
    }
  });
}

(Obviously the code has been stripped to the bare minimum to make my intentions clearer)

What I want to do is return a bool true when it gets found, so I can go

if (connected()) {
  // process data
}

(obviously the data is stored in a global variable.)

Is there any way to achieve this?

EDIT

I could create a 'pause' function:

function pauseScript(ms) {
  ms += new Date().getTime();
  while (new Date().getTime() < ms) {}
}

Then change my code to exclude setTimeout() (and include some other stuff)

function connected() {
  var value;
  $.ajax({
    async: false,
    success : function(d) {
      if (d.success) {
        // set data
          value = true;
      } else {
        pauseScript(200);
        value = connected();
      }
    }
  });
  return value;
}

However this feels a little hacky!

5
  • 3
    Welcome to the wonderful world of async! You can't do that. Commented Jul 1, 2012 at 23:15
  • 3
    Why can't I down-vote comments? Commented Jul 1, 2012 at 23:17
  • That's not the way to think about it! Nothing is impossible! Commented Jul 1, 2012 at 23:18
  • Don't try to pause using a while loop, you'll just lock the browser using 100% CPU (or the maximum browser can get) so may as well use a synchronous call. Commented Jul 1, 2012 at 23:47
  • Yeah, I did say it seemed hacky and I didn't even try it, I was just looking into pausing scripts, Commented Jul 1, 2012 at 23:52

2 Answers 2

2

For doing that the best should continue in async mode :

function connected(callback) {
  $.ajax({
    success : function(d) {
      if (d.success) {
        callback();
      } else {
        setTimeout(function() {
          connected(callback);
        }, 200);
      }
    }
  });
}

//Using :
connected(function(){
  //Here i finish my scan result
});

Maybe, i'm not so sure in this case, you will need to bind this (I mean binding scope) : http://www.robertsosinski.com/2009/04/28/binding-scope-in-javascript/

Sign up to request clarification or add additional context in comments.

1 Comment

The best part is, in my if statement I am calling another function so I can just put the function as the callback! Thanks!
1

If the browser you're targeting supports JavaScript 1.7 or later (At the time of writing, I think only Gecko browsers do) then you can utilize generators to do this. Let's first make your connect function asynchronous (and simpler for testing):

function connectAsync(callback) {
    // Pretend we always succeed after a second.
    // You could use some other asynchronous code instead.
    setTimeout(callback, 1000, true);
}

Now wrap your calling code into a function. (It must be in a function.)

function main() {
    console.log("Connecting...");
    if(!(yield connect())) {
        console.log("Failed to connect.");
        yield; return;
    }
    console.log("Connected.");
    yield;
}

Note all of the yields everywhere. This is part of the magic. We need a yield when we call connect and a yield before every function exit point.

Now we need to define connect. You may have a bunch of asynchronous functions you may want to synchronize, so let's make a function that makes functions synchronous.

function synchronize(async) {
    return function synchronous() {
        return {func: async, args: arguments};
    };
}

And then we can create connect from connectAsync with it:

var connect = synchronize(connectAsync);

Now we need to make a function that runs all of this magic. Here it is:

function run(sync) {
    var args = Array.prototype.slice.call(arguments);
    var runCallback = args.length ? args[args.length - 1] : null;
    if(args.length) {
        args.splice(args.length - 1, 1);
    }
    var generator = sync.apply(window, args);
    runInternal(generator.next());
    function runInternal(value) {
        if(typeof value === 'undefined') {
            if(runCallback) {
                return runCallback();
            }else{
                return;
            }
        }
        function callback(result) {
            return runInternal(generator.send(result));
        }
        var args = Array.prototype.slice.call(value.args);
        args.push(callback);
        value.func.apply(window, args);
    }
}

Now you can run your main function with this magic:

run(main);

I should note that it is no longer possible for main to return a value. run can take a second argument: a callback to be called when the synchronous function has completed. To pass arguments to main (say, 1, 2, and 3), you would call it like this:

run(main, 1, 2, 3, callback);

If you do not want a callback, pass null or undefined.


To try this out, you must set the type of the script tag to text/javascript; version=1.7. JavaScript 1.7 adds new keywords (like yield), so it is backwards-incompatible, and thus, must have some way to distinguish itself from older code.

For more information on generators, see this page on MDN. For more information on coroutines, see the article on Wikipedia.

With all of that said, this is not practical because of the limited support of JavaScript 1.7. Additionally, it is not common to see this sort of code, so it may be difficult to understand and maintain. I would recommend just using an asynchronous style, as demonstrated in Deisss's answer. It is neat that it's possible to do this, however.

1 Comment

You summed it up yourself! Interesting but, at the current time, impractical. Great answer though!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.