jQuery 1.5带来了新的Deferred对象和附加方法.when、.Deferred和. _deferred。
对于那些以前没有使用过. deferred的人,我已经为它注释了源代码。
这些新方法的可能用途是什么,我们如何将它们纳入模式?
我已经阅读了API和源代码,所以我知道它是做什么的。我的问题是我们如何在日常代码中使用这些新特性?
我有一个简单的缓冲区类示例,它按顺序调用AJAX请求。(上一个结束后下一个开始)。
/* Class: Buffer
* methods: append
*
* Constructor: takes a function which will be the task handler to be called
*
* .append appends a task to the buffer. Buffer will only call a task when the
* previous task has finished
*/
var Buffer = function(handler) {
var tasks = [];
// empty resolved deferred object
var deferred = $.when();
// handle the next object
function handleNextTask() {
// if the current deferred task has resolved and there are more tasks
if (deferred.isResolved() && tasks.length > 0) {
// grab a task
var task = tasks.shift();
// set the deferred to be deferred returned from the handler
deferred = handler(task);
// if its not a deferred object then set it to be an empty deferred object
if (!(deferred && deferred.promise)) {
deferred = $.when();
}
// if we have tasks left then handle the next one when the current one
// is done.
if (tasks.length > 0) {
deferred.done(handleNextTask);
}
}
}
// appends a task.
this.append = function(task) {
// add to the array
tasks.push(task);
// handle the next task
handleNextTask();
};
};
我正在寻找. deferred和.when的演示和可能的用法。
看到. _deferred的例子也很不错。
链接到新的jQuery。Ajax源代码的例子是作弊。
我特别感兴趣的是,当我们抽象出一个操作是同步完成还是异步完成时,可以使用哪些技术。
可以用deferred来代替互斥量。这本质上与多个ajax使用场景相同。
互斥锁
var mutex = 2;
setTimeout(function() {
callback();
}, 800);
setTimeout(function() {
callback();
}, 500);
function callback() {
if (--mutex === 0) {
//run code
}
}
递延
function timeout(x) {
var dfd = jQuery.Deferred();
setTimeout(function() {
dfd.resolve();
}, x);
return dfd.promise();
}
jQuery.when(
timeout(800), timeout(500)).done(function() {
// run code
});
当只使用Deferred作为互斥量时,要注意性能影响(http://jsperf.com/deferred-vs-mutex/2)。虽然Deferred提供的便利和额外的好处是值得的,但在实际(基于用户驱动的事件)使用中,性能影响不应该很明显。
Another use that I've been putting to good purpose is fetching data from multiple sources. In the example below, I'm fetching multiple, independent JSON schema objects used in an existing application for validation between a client and a REST server. In this case, I don't want the browser-side application to start loading data before it has all the schemas loaded. $.when.apply().then() is perfect for this. Thank to Raynos for pointers on using then(fn1, fn2) to monitor for error conditions.
fetch_sources = function (schema_urls) {
var fetch_one = function (url) {
return $.ajax({
url: url,
data: {},
contentType: "application/json; charset=utf-8",
dataType: "json",
});
}
return $.map(schema_urls, fetch_one);
}
var promises = fetch_sources(data['schemas']);
$.when.apply(null, promises).then(
function () {
var schemas = $.map(arguments, function (a) {
return a[0]
});
start_application(schemas);
}, function () {
console.log("FAIL", this, arguments);
});
我只是在实际代码中使用了Deferred。在jQuery终端项目中,我有函数exec调用用户定义的命令(就像他进入并按下enter),我添加了延迟到API和调用exec数组。是这样的:
terminal.exec('command').then(function() {
terminal.echo('command finished');
});
or
terminal.exec(['command 1', 'command 2', 'command 3']).then(function() {
terminal.echo('all commands finished');
});
命令可以运行异步代码,exec需要按顺序调用用户代码。我的第一个api使用暂停/恢复调用对,在新的api中,当用户返回承诺时,我调用那些自动。所以用户代码可以直接使用
return $.get('/some/url');
or
var d = new $.Deferred();
setTimeout(function() {
d.resolve("Hello Deferred"); // resolve value will be echoed
}, 500);
return d.promise();
我使用这样的代码:
exec: function(command, silent, deferred) {
var d;
if ($.isArray(command)) {
return $.when.apply($, $.map(command, function(command) {
return self.exec(command, silent);
}));
}
// both commands executed here (resume will call Term::exec)
if (paused) {
// delay command multiple time
d = deferred || new $.Deferred();
dalyed_commands.push([command, silent, d]);
return d.promise();
} else {
// commands may return promise from user code
// it will resolve exec promise when user promise
// is resolved
var ret = commands(command, silent, true, deferred);
if (!ret) {
if (deferred) {
deferred.resolve(self);
return deferred.promise();
} else {
d = new $.Deferred();
ret = d.promise();
ret.resolve();
}
}
return ret;
}
},
Dalyed_commands用于恢复函数,使用所有的Dalyed_commands再次调用exec。
和部分命令函数(我已经剥离了不相关的部分)
function commands(command, silent, exec, deferred) {
var position = lines.length-1;
// Call user interpreter function
var result = interpreter.interpreter(command, self);
// user code can return a promise
if (result != undefined) {
// new API - auto pause/resume when using promises
self.pause();
return $.when(result).then(function(result) {
// don't echo result if user echo something
if (result && position === lines.length-1) {
display_object(result);
}
// resolve promise from exec. This will fire
// code if used terminal::exec('command').then
if (deferred) {
deferred.resolve();
}
self.resume();
});
}
// this is old API
// if command call pause - wait until resume
if (paused) {
self.bind('resume.command', function() {
// exec with resume/pause in user code
if (deferred) {
deferred.resolve();
}
self.unbind('resume.command');
});
} else {
// this should not happen
if (deferred) {
deferred.resolve();
}
}
}
ehyds的答案将不起作用,因为它缓存了响应数据。它应该缓存jqXHR,这也是一个Promise。
下面是正确的代码:
var cache = {};
function getData( val ){
// return either the cached value or an
// jqXHR object (which contains a promise)
return cache[ val ] || $.ajax('/foo/', {
data: { value: val },
dataType: 'json',
success: function(data, textStatus, jqXHR){
cache[ val ] = jqXHR;
}
});
}
getData('foo').then(function(resp){
// do something with the response, which may
// or may not have been retreived using an
// XHR request.
});
Julian d的答案是正确的,是一个更好的解决方案。