I have created following javascript code:
var index, _i;
var dfd = new $.Deferred();
dfd.resolve();
for (index = _i = 0; _i < 10; index = ++_i) {
$.when($, dfd).done(function() {
console.log('ping');
promise = (function(index) {
setTimeout(function() {
console.log('pong');
dfd.resolve();
}, 3000);
dfd.reject();
})(index);
})
}
in console I see following result
ping
ping
ping
ping
ping
ping
ping
ping
ping
ping
pong
pong
pong
pong
pong
pong
pong
pong
pong
pong
But I want to achieve
ping
pong
ping
pong
....
Where do I wrong and how to rewrite code acording my desired behavour?
Here's a way to do it:
function go(val, t) {
var def = $.Deferred();
setTimeout(function() {
log(val);
def.resolve();
}, t);
return def.promise();
}
function run(numCycles, values, delay) {
function next() {
if (numCycles > 0) {
go(values[0], delay).then(function() {
return go(values[1], delay);
}).then(function() {
--numCycles;
next();
});
}
}
next();
}
run(10, ["ping", "pong"], 500);
Working demo: http://jsfiddle.net/jfriend00/g0Lxm3ws/
Conceptually, here's how this works:
- The function
go()
outputs one message after a timer. It returns a promise that is resolved when the message has been output. - The function
run()
accepts a number of cycles and an array of two values to alternate between. - The function
next()
checks to see if there are any more cycles to run and if so, chains twogo()
operations together and when the last one is done, decrements the number of cycles and then callsnext()
again.
There are all sorts of issues with your implementation:
- A given deferred or promise can only be resolved or rejected once.
$.when()
takes a list of promises so$.when($, dfd)
is just wrong.$.when()
is not even needed when there is only one promise as you can just use.then()
directly on the promise.- Your
for
loop won't pause between promises so all your"ping"
messages will come out immediately and then the"pong"
messages will come out later.
FYI, if you want to cycle through an arbitrary array of values, you can do so like this:
function go(val, t) {
var def = $.Deferred();
setTimeout(function() {
log(val);
def.resolve();
}, t);
return def.promise();
}
function run(numCycles, values, delay) {
function next() {
if (numCycles > 0) {
// create initial resolved promise
// for start of a .reduce() chain
var d = $.Deferred().resolve().promise();
values.reduce(function(p, val){
return p.then(function() {
return go(val, delay);
});
}, d).then(function() {
--numCycles;
next();
});
}
}
next();
}
run(5, ["tic", "tac", "toe"], 500);
Working demo: http://jsfiddle.net/jfriend00/1ckb6sg6/
I must say I've no idea how you've arrived at that sort of code or what you're really trying to achieve. However, to at least answer the first of your questions (what's going wrong?):
Once a deferred is resolved, it cannot (I believe) later be rejected. They are intended as one-time-use objects, as discussed in this question.
Secondly, you are getting all the pings before the pongs because the mechanism by which the pings are output is a synchronous one (the loop). The mechanism by which the pongs are output, however, is an asynchronous one - the timeout. In effective terms, therefore, the pings are all output in the same procedural instance, whereas the pongs don't kick in till 3 seconds later - long after the pings have been output.
来源:https://stackoverflow.com/questions/26691226/ping-pong-behaviour-using-deffered