我写了一个jQuery插件,我称之为.thenst。这只是一个then,继续下一步使用setTimeout。我不确定它总体上对人类有多大用处,但它解决了这个问题。
这是http://jsfiddle.net/ubershmekel/8mtbM/2/的演示
插件代码
(function ($) {
/*
Give deferred objects a `.thenst` function which chains like `then`
but adds a `setTimeout` in between to allow for UI responsiveness.
This is extra useful for chaining ~1-second long functions.
*/
var origDeferred = $.Deferred;
$.Deferred = function (func) {
var thenst = function (fnDone, fnFail, fnProgress) {
var newDef = $.Deferred();
var args;
var runsAfterSetTimeout = function () {
newDef.then(fnDone);
newDef.resolveWith(newDef, args);
};
def.then(function () {
args = arguments;
setTimeout(runsAfterSetTimeout, 0)
}, fnFail, fnProgress);
return newDef;
};
// Hooking jQuery's Deferred is hard because we have to modify Deferred()
// and Deferred.promise() though a Deferred has all the methods of a promise.
// It would be nicer if we could only extend the `promise ` object.
var def = origDeferred(func);
def.thenst = thenst;
var origPromise = def.promise;
def.promise = function (obj) {
obj = origPromise(obj);
obj.thenst = thenst;
return obj;
}
return def;
};
}(jQuery));
这是示例页面,其中包含示例页面,以防 jsfiddle 失败。
<!DOCTYPE html>
<html lang="en-us">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" type="text/javascript"></script>
</head>
<body>
<p>Notice how fillLinear/fillDefer don't let you see the numbers were cleared. The button doesn't become depressed. And you don't see the numbers updating as they're pinned. fillSetTimeout works well but is hard to read and write. The performance difference between fillSetTimeout and fillThenst is inconsisntent and is probably garbage-collection and redrawing-timing related.</p>
<button onclick="fillLinear()">fillLinear</button>
<button onclick="fillDefer()">fillDefer</button>
<button onclick="fillSetTimeout()">fillSetTimeout</button>
<button onclick="fillThenst()">fillThenst</button>
<div id="panel"></div>
<script>
(function ($) {
/*
Give deferred objects a `.thenst` function which chains like `then`
but adds a `setTimeout` in between to allow for UI responsiveness.
This is extra useful for chaining ~1-second long functions.
*/
var origDeferred = $.Deferred;
$.Deferred = function (func) {
var thenst = function(fnDone, fnFail, fnProgress) {
var newDef = $.Deferred();
var args;
var runsAfterSetTimeout = function() {
/*if ($.isFunction(fnDone)) {
// call the function and report it's done
fnDone.apply(newDef, args);
} else {
// fnDone should be a promise object like:
// $.Deferred().thenst($.ajax());
// We want to trigger that promise to run, I'm not sure if this is the way to do that.
// Though this point is kinda moot for $.ajax as it would fire off at instantiation.
newDef.then(fnDone);*/
newDef.then(fnDone);
newDef.resolveWith(newDef, args);
};
def.then(function() { args = arguments; setTimeout(runsAfterSetTimeout, 0) }, fnFail, fnProgress);
return newDef;
};
// Hooking jQuery's Deferred is hard because we have to modify Deferred()
// and Deferred.promise() though a Deferred has all the methods of a promise.
// It would be nicer if we could only extend the `promise ` object.
var def = origDeferred(func);
def.thenst = thenst;
var origPromise = def.promise;
def.promise = function(obj) {
obj = origPromise(obj);
obj.thenst = thenst;
return obj;
}
return def;
};
}(jQuery));
var setTimeoutDelay = 50; // I noticed that 0 releases the UI in IE10 but not fully in Chrome31 or FF25.
var time = function() {
return (new Date).getTime();
}
busy = function() {
var start = time();
while (time() - start < 1000) {
}
console.log('dn ' + time());
}
pin = function(i) {
$('#panel').append($('<h1>' + i + '</h1>'));
}
bclear = function() {
$('#panel').html('');
}
fillLinear = function() {
bclear();
pin(0);
busy();
pin(1);
busy();
pin(2);
busy();
pin(3);
}
fillDefer = function() {
bclear();
pin(0);
var def = $.Deferred();
def.then(function() {
busy();
pin(1);
}).then(function() {
busy();
pin(2);
}).then(function() {
busy();
pin(3);
});
def.resolve();
}
fillSetTimeout = function() {
bclear();
pin(0);
setTimeout(function() {
busy();
pin(1);
setTimeout(function() {
busy();
pin(2);
setTimeout(function() {
busy();
pin(3);
}, setTimeoutDelay);
}, setTimeoutDelay);
}, setTimeoutDelay);
}
fillThenst = function() {
bclear();
pin(0);
var def = $.Deferred();
def.thenst(function() {
busy();
pin(1);
}).thenst(function() {
busy();
pin(2);
}).thenst(function() {
busy();
pin(3);
});
def.resolve();
}
</script>
</body>
</html>
PS thenst 代表 then-setTimeout。听起来没有创意。