JavaScript set timeout loop return value

心不动则不痛 提交于 2021-02-08 09:07:30

问题


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!


回答1:


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/




回答2:


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.



来源:https://stackoverflow.com/questions/11286340/javascript-set-timeout-loop-return-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!