什么是延迟对象?

JQuery 1.5添加了“延迟对象”。它们是什么,它们具体做什么?

50766 次浏览

递延对象

从 jQuery 1.5开始,Deferred 对象提供了一种方法,可以将多个回调注册到自管理的回调队列中,适当地调用回调队列,并中继任何同步或异步函数的成功或失败状态。

递延方法:

  • 完成()
    • 添加要在解析 Deferred 对象时调用的处理程序。
  • Fail ()
    • 添加在拒绝 Deferred 对象时要调用的处理程序。
  • 被拒绝()
    • 确定延迟对象是否已被拒绝。
  • IsResolved ()
    • 确定是否解析了延迟对象。
  • 推迟。拒绝()
    • 拒绝延迟对象并使用给定的参数调用任何故障回调。
  • RejectWith ()
    • 拒绝延迟对象并使用给定的上下文和参数调用任何故障回调。
  • ()
    • 解析一个 Deferred 对象并使用给定的参数调用任何 doneCallback。
  • ()
    • Resolve a Deferred object and call any doneCallbacks with the given context and args.
  • 推迟,然后()
    • 添加要在解析或拒绝 Deferred 对象时调用的处理程序。

在行动中推迟:

$.get("test.php").done(
function(){ alert("$.get succeeded"); }
);


$.get("test.php")
.done(function(){ alert("$.get succeeded"); })
.fail(function(){ alert("$.get failed!"); });

现有的 ajax ()方法回调函数似乎可以链接,而不是在设置中声明:

var jqxhr = $.ajax({ url: "example.php" })
.success(function() { alert("success"); })
.error(function() { alert("error"); })
.complete(function() { alert("complete"); });

工作示例 来自 Eric Hynds 的博客文章: http://jsfiddle.net/ehynds/Mrqf8/


JqXHR

在 jQuery 1.5中,$。Ajax ()方法返回 jXHR 对象,它是 XMLHTTPRequest 对象的超集。有关更多信息,请参见 $。Ajax 入口


来自 JQUERY 1.5发布:

递延对象

随着 Ajax 的重写 模块中引入了一个新的特性 这也是公开的 收听: 递延对象。这个 API 允许您使用 return 价值观可能不会立即 存在(如返回结果) 来自异步 Ajax 请求)。 此外,它给你的能力 附加多个事件处理程序 (一些以前没有的东西 可以在 Ajax API 中实现)。

另外你可以自己做 使用公开的 JQuery. 延迟. 更多信息 有关这个 API 的资料,请参阅 延迟对象 文档。

Eric Hynds 写了一篇好文章 tutorial on Using Deferreds in jQuery 1.5

与其告诉你它是做什么的,不如让你看看它是做什么的,然后解释一下。

JQuery 1.5相关源代码的一个副本,带有注释,解释它在做什么。我认为这些评论大部分是正确的。

This may be of benefit

// promiseMethods. These are the methods you get when you ask for a promise.
// A promise is a "read-only" version
// fullMethods = "then done fail resolve resolveWith reject rejectWith isResolve    isRejected promise cancel".split(" ")
// As you can see it removes resolve/reject so you can't actaully trigger a
// anything on the deferred object, only process callbacks when it "finishes".
promiseMethods = "then done fail isResolved isRejected promise".split(" "),


// Create a simple deferred (one callbacks list)
/* Class: _Deferred.
*  methods: done, resolve, resolveWith, isResolved
*  internal method: cancel
*
*  Basically allows you to attach callbacks with the done method.
*  Then resolve the deferred action whenever you want with an argument.
*  All the callbacks added with done will be called with the resolved argument
*  Any callbacks attached after resolvement will fire immediatly.
*
*  resolveWith allows you to set the this scope in the callbacks fired.
*
*  isResolved just checks whether it's resolved yet.
*
*  cancel blocks resolve/resolveWith from firing. the methods added throug
*  done will never be called
*/
_Deferred: function () {
var // callbacks list
callbacks = [],
// stored [ context , args ]
// stores the context & args that .resolve was called with
fired,
// to avoid firing when already doing so
firing,
// flag to know if the deferred has been cancelled
// in Deferred cancel gets called after the first resolve call
cancelled,
// the deferred itself
deferred = {


// done( f1, f2, ...)
done: function () {
if (!cancelled) {
var args = arguments,
i, length,
// elem in callback list
elem,
// type of elem in callback list
type,
// cached context & args for when done is called
// after resolve has been
_fired;
// If resolve has been called already
if (fired) {
// mark it locally
_fired = fired;
// set fired to 0. This is neccesary to handle
// how done deals with arrays recursively
// only the original .done call handles fired
// any that unwrap arrays and call recursively
// dont handle the fired.
fired = 0;
}
// for each function append it to the callback list
for (i = 0, length = args.length; i < length; i++) {
elem = args[i];
type = jQuery.type(elem);
// if argument is an array then call done recursively
// effectively unwraps the array
if (type === "array") {
// def.done([f1, f2, f3]) goes to
// def.done(f1, f2, f3) through the apply
deferred.done.apply(deferred, elem);
} else if (type === "function") {
// if its a function add it to the callbacks
callbacks.push(elem);
}
}
// if it's already been resolved then call resolveWith using
// the cahced context and arguments to call the callbacks
// immediatly
if (_fired) {
deferred.resolveWith(_fired[0], _fired[1]);
}
}
return this;
},


// resolve with given context and args
resolveWith: function (context, args) {
// if its been cancelled then we can't resolve
// if it has fired then we can't fire again
// if it's currently firing then we can't fire. This check is
// there because of the try finally block. It ensures we
// cant call resolve between the try & finally in the catch phase.
if (!cancelled && !fired && !firing) {
firing = 1;
// try block because your calling external callbacks
// made by the user which are not bugfree.
// the finally block will always run no matter how bad
// the internal code is.
try {
while (callbacks[0]) {
callbacks.shift().apply(context, args);
}
// cache the content and arguments taht have been called
// and set firing to false.
} finally {
fired = [context, args];
firing = 0;
}
}
return this;
},


// resolve with this as context and given arguments
// just maps to resolveWith, this sets the this scope as normal
// maps to this.promise which is the read only version of Deferred.
resolve: function () {
deferred.resolveWith(jQuery.isFunction(this.promise) ? this.promise() :
this, arguments);
return this;
},


// Has this deferred been resolved?
// checks whether it's firing or if it has fired.
isResolved: function () {
return !!(firing || fired);
},


// Cancels the action. To be used internally
cancel: function () {
cancelled = 1;
callbacks = [];
return this;
}
};


return deferred;
},
/* Class: Deferred.
*  methods: then, done, fail, resolve, reject, resolveWith, rejectWith, isResolved,
isRejected, promise
*
*  then is a shortcut for both assigning done & fail in one function.
*
*  This one has two underlying lists with different semantic meanings. You
*  can bind to both the done callbacks and the fail callbacks then either
*  resolve or reject your Deferred object.
*
*  You can check whether it has been resolved or rejected. useful to see
*  Afterwards which one has happened.
*
*  Call .promise to return a new object which doesn't have the resolve/reject
*  methods on it. This means you can only bind to it and not resolve/reject it.
*  This is effectively read-only.
*
*/
// Full fledged deferred (two callbacks list)
Deferred: function (func) {
// the main deferred which deals with the success callbacks
var deferred = jQuery._Deferred(),
// the failure deferred which deals with the rejected callbacks
failDeferred = jQuery._Deferred(),
// the read only promise is cached.
promise;
// Add errorDeferred methods, then and promise
jQuery.extend(deferred, {
// def.then([f1, f2, ...], [g1, g2, ...] is a short hand for
// def.done([f1, f2, ...])
// def.fail([g1, g2, ...])
then: function (doneCallbacks, failCallbacks) {
// fail exists here because this code will only run after
// deferred has been extended.
deferred.done(doneCallbacks).fail(failCallbacks);
return this;
},
// map def.fail to the second underlying deferred callback list
// map all the other methods for rejection/failure to the underlying
// failDeffered object so that Deferred has two callback lists stored
// internally.
fail: failDeferred.done,
rejectWith: failDeferred.resolveWith,
reject: failDeferred.resolve,
isRejected: failDeferred.isResolved,
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
// no clue what to do with "i"
promise: function (obj, i /* internal */ ) {
// if no argument is passed then just extend promise
if (obj == null) {
// if cached return the cache.
if (promise) {
return promise;
}
// set promise & arg to be {}
promise = obj = {};
}
// for each promiseMethods in the read only promise list
i = promiseMethods.length;
while (i--) {
// set the deferred method on the object
obj[promiseMethods[i]] = deferred[promiseMethods[i]];
}
// returns the "read-only" deferred without
// resolve, resolveWith, reject & rejectWith.
// So you cant "resolve" it but only add "done" functions
return obj;
}
});
// Make sure only one callback list will be used
// if either resolve or reject is called cancel both.
// this means that the one that has been called cant be called again
// and the other one will never be called. So only the done or the fail
// methods will ever be called
deferred.then(failDeferred.cancel, deferred.cancel);
// Don't mess with cancel!
// Unexpose cancel
delete deferred.cancel;
// Call given func if any
// function argument to be called. This was passed in. Allows you to
// handle the deferred object after creating a new one, both as this scope
// and as a new argument.
if (func) {
func.call(deferred, deferred);
}
return deferred;
},


/* Method: when
* Arguments: none OR 1 of type(any & !deferred) OR n of type(deferred).
*
* If no arguments are passed then it gets resolved immediatly. A good way to
* call multiple callback functions? Don't really know a good use of $.when()
*
* If one argument is passed and its not a deferred object then it resolves
* immediatly and passes that argument to all the done callbacks attached.
*
* if n arguments are passed of type deferred object then the the done callbacks
* will only fire if all of them succeed. If a single one fails then the
* fail callbacks fire.
*
* Returns a promise read-only deferred object
*/
// Deferred helper
when: function (object) {
var args = arguments,
length = args.length,
// If you pass in a deferred object then set deferred to be the promise
// if you pass in anything else then set deferred to be a new deferred
deferred = length <= 1 && object && jQuery.isFunction(object.promise) ?
object :
jQuery.Deferred(),
// cache the promise
promise = deferred.promise(),
// store an array
resolveArray;


// if multiple objects are passed in
if (length > 1) {
// create an arrey to store of values.
resolveArray = new Array(length);
// for each object that we wait on
jQuery.each(args, function (index, element) {
// when that object resolves then
jQuery.when(element).then(function (value) {
// store value in the array or store an array of values in it
resolveArray[index] = arguments.length > 1 ? slice.call(arguments, 0) :
value;
// if length === 1 then we finished calling them all
if (!--length) {
// resolve the deferred object with the read only promise
// as context and the resolved values array as the argument
deferred.resolveWith(promise, resolveArray);
}
// if any fail then we reject or deferred
}, deferred.reject);
});
// if deferred was newly created but there was only one argument then
// resolve it immediatly with the argument.
} else if (deferred !== object) {
deferred.resolve(object);
}
// return the read-only deferred.
return promise;
},

如果我说错了请纠正我,但是最近我突然意识到它本质上是一个异步任务运行器。这个承诺是一个结果合同,确保你得到... ... 一些东西,但不能保证你什么时候能得到它。

在 Javascript 中工作时,我们遇到函数调用是异步的情况。也就是说,calee 函数的(比如说 X)流不等待被调用的异步函数(比如说 Y)。典型的例子是,当我们调用服务器从数据库或 HTML 页面获取一些数据时。如果这些调用不是异步的,那么用户界面将停留在等待服务器响应的状态。当您希望按顺序执行某些内容时,这种异步性质会导致问题,例如,您希望在 Y (异步)执行或获取数据之后打印某些内容。这里 jQuery 为我们提供了延迟对象。基本上,jQuery 处理了我们通常为解决这种情况而编写的所有样板代码。下面是一个简单的例子:

  $.ajax({
...
}).done(function(){
//write here what you wish to do when this ajax call is success
}).fail(function(){
//write here what you wish to do on failure of this ajax call
}); //see more on jQuery Deferred page

You can write your own deferred (asynch) function

function DoSomethingTimeConsumingAsynch(){
var deferred = $.Deferred();


_.defer(function(){ //I am using underscore, you can also use setTimeout
...
deferred.resolve();//When the process is done successfully
...
deferred.reject(); //When the process has failed
});
return deferred;
}


//HEre how to use your own asynch function
DoSomethingTimeConsumingAsynch()
.done(function(){
//this will be invoked on success
})
.fail(function(){
//this will be invoked on failure
})

I hope this helped.