jQuery 1.5 เพิ่ม "Deferred Objects" พวกเขาคืออะไรและทำอะไรกันแน่?
jQuery 1.5 เพิ่ม "Deferred Objects" พวกเขาคืออะไรและทำอะไรกันแน่?
คำตอบ:
วัตถุรอการตัดบัญชี
ตั้งแต่ jQuery 1.5 อ็อบเจ็กต์ 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 สำหรับข้อมูลเพิ่มเติมโปรดดูส่วน jXHR ของรายการ $ .ajax
จากJQUERY 1.5 RELEASED :
วัตถุเบี่ยงเบน
พร้อมกับเขียนของโมดูลอาแจ็กซ์คุณลักษณะใหม่ที่ได้รับการแนะนำที่ถูกสร้างขึ้นมานอกจากนี้ยังมีสาธารณะ: วัตถุรอตัดบัญชี API นี้ช่วยให้คุณสามารถทำงานกับค่าส่งคืนที่อาจไม่ปรากฏในทันที (เช่นผลลัพธ์ที่ส่งคืนจากคำขอ Ajax แบบอะซิงโครนัส) นอกจากนี้ยังช่วยให้คุณสามารถแนบตัวจัดการเหตุการณ์ได้หลายตัว (สิ่งที่ไม่เคยทำได้มาก่อนใน Ajax API)
นอกจากนี้คุณสามารถสร้างวัตถุรอการตัดบัญชีของคุณเองโดยใช้ jQuery.Deferred ที่เปิดเผย ข้อมูลเพิ่มเติมเกี่ยวกับ API นี้สามารถพบได้ในเอกสารของ Deferred Object
เอริค Hynds ได้เขียนขึ้นกวดวิชาที่ดีในการใช้ Deferreds ใน jQuery 1.5
แต่จะบอกคุณว่ามันทำอะไรฉันจะแสดงให้คุณเห็นว่ามันทำอะไรและอธิบายมัน
สำเนาแหล่งที่มาที่เกี่ยวข้องของ jQuery 1.5 พร้อมคำอธิบายประกอบที่อธิบายว่ามันทำอะไร ผมว่าความคิดเห็นส่วนใหญ่ถูกต้อง
นี่อาจเป็นประโยชน์
// 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;
},
Problem solved
. Btw นั่นคือแหล่งที่มา 1.5beta ฉันคิดว่ามีการเปลี่ยนแปลงบางอย่างใน 1.6
แก้ไขฉันถ้าฉันผิด แต่เมื่อไม่นานมานี้ฉันคลิกว่ามันเป็น Asynchronous Task Runner สัญญาเป็นสัญญาผลลัพธ์เพื่อให้แน่ใจว่าคุณจะได้รับ ... บางอย่าง แต่ไม่มีการรับประกันว่าคุณจะได้รับเมื่อใด
ในขณะที่ทำงานใน Javascript เราพบสถานการณ์ที่การเรียกใช้ฟังก์ชันเป็นแบบอะซิงโครนัส นั่นคือโฟลว์ของฟังก์ชัน calee (สมมติว่า X) ไม่รอให้เรียกว่าฟังก์ชันอะซิงโครนัส (สมมติว่า Y) ตัวอย่างทั่วไปคือเมื่อเราโทรไปยังเซิร์ฟเวอร์เพื่อดึงข้อมูลบางส่วนจากฐานข้อมูลหรือเพจ HTML หากการโทรเหล่านั้นไม่ใช่แบบอะซิงโครนัสอินเทอร์เฟซผู้ใช้จะค้างรอให้เซิร์ฟเวอร์ตอบกลับ ลักษณะอะซิงโครนัสนี้ทำให้เกิดปัญหาเมื่อคุณต้องการดำเนินการตามลำดับตัวอย่างเช่นคุณต้องการพิมพ์บางสิ่งหลังจาก Y (asynch) ดำเนินการเสร็จสิ้นหรือดึงข้อมูลเสร็จแล้ว ที่นี่ jQuery ให้ Deffered Object แก่เรา โดยพื้นฐานแล้ว 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
คุณสามารถเขียนฟังก์ชันรอการตัดบัญชี (asynch) ของคุณเองได้
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
})
ฉันหวังว่านี่จะช่วยได้