问题
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 yield
s 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