Đối tượng trả chậm là gì?


Câu trả lời:


101

Đối tượng bị hoãn lại

Kể từ jQuery 1.5, đối tượng Deferred cung cấp một cách để đăng ký nhiều lệnh gọi lại vào hàng đợi gọi lại tự quản lý, gọi hàng đợi gọi lại nếu thích hợp và chuyển tiếp trạng thái thành công hoặc thất bại của bất kỳ hàm đồng bộ hoặc không đồng bộ nào.

Phương thức hoãn lại:

  • deferred.done ()
    • Thêm trình xử lý được gọi khi đối tượng Deferred được giải quyết.
  • deferred.fail ()
    • Thêm trình xử lý sẽ được gọi khi đối tượng Deferred bị từ chối.
  • deferred.isRejected ()
    • Xác định xem đối tượng Trì hoãn đã bị từ chối hay chưa.
  • deferred.isResolved ()
    • Xác định xem một đối tượng Bị hoãn đã được giải quyết hay chưa.
  • deferred.reject ()
    • Từ chối một đối tượng Deferred và gọi bất kỳ failCallback nào với các args đã cho.
  • deferred.rejectWith ()
    • Từ chối một đối tượng Deferred và gọi bất kỳ failCallback nào với ngữ cảnh và args đã cho.
  • deferred.resolve ()
    • Giải quyết một đối tượng Deferred và gọi bất kỳ doneCallback nào với các args đã cho.
  • deferred.resolveWith ()
    • Giải quyết một đối tượng Deferred và gọi mọi doneCallbacks với ngữ cảnh và args đã cho.
  • deferred.then ()
    • Thêm trình xử lý được gọi khi đối tượng Deferred được giải quyết hoặc bị từ chối.

Hành động hoãn lại:

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

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

Và có vẻ như các lệnh gọi lại phương thức ajax () hiện tại có thể được xâu chuỗi chứ không phải được khai báo trong cài đặt:

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

Ví dụ làm việc Từ bài đăng trên blog của Eric Hynds : http://jsfiddle.net/ehynds/Mrqf8/


jqXHR

Kể từ jQuery 1.5, phương thức $ .ajax () trả về đối tượng jXHR, là một tập siêu của đối tượng XMLHTTPRequest. Để biết thêm thông tin, hãy xem phầnjXHR của mục nhập $ .ajax


Từ JQUERY 1.5 LIÊN QUAN :

ĐỐI TƯỢNG ĐÃ XÁC ĐỊNH

Cùng với việc viết lại các module Ajax một tính năng mới được giới thiệu tác phẩm đã được công bố công khai: Đối tượng thu nhập hoãn lại . API này cho phép bạn làm việc với các giá trị trả về có thể không xuất hiện ngay lập tức (chẳng hạn như kết quả trả về từ một yêu cầu Ajax không đồng bộ). Ngoài ra, nó cung cấp cho bạn khả năng đính kèm nhiều trình xử lý sự kiện (điều mà trước đây trong API Ajax không thể thực hiện được).

Ngoài ra, bạn có thể tạo các đối tượng trì hoãn của riêng mình bằng cách sử dụng jQuery.Deferred được tiếp xúc. Thông tin thêm về API này có thể được tìm thấy trong tài liệu Đối tượng được hoãn lại .

Eric Hynds đã viết một bài hướng dẫn hay về việc sử dụng Deferreds trong jQuery 1.5 .


19
Xin giải thích thêm. Làm cách nào để tạo Đối tượng hoãn lại tùy chỉnh của riêng tôi. Họ làm việc như thế nào?
user113716

3
Thực ra tôi đang rất nghiêm túc. Đây là một câu hỏi hay về một tính năng hoàn toàn mới. Tôi không biết chúng hoạt động như thế nào và tôi nghĩ sẽ rất tốt nếu StackOverflow giải thích rõ câu hỏi này cho những ai sẽ hỏi về nó trong tương lai.
user113716

1
cập nhật: Tôi nghĩ rằng định nghĩa về "Bị hoãn" ở trên mà tôi đã thêm ở trên cùng mang lại một cái nhìn rõ ràng hơn về những gì nó thực sự đang làm. Nó dường như thiên về khả năng xâu chuỗi các lệnh gọi lại thay vì phải khai báo chúng trong các cài đặt được chuyển vào một hàm.
hunter

1
@Hunter Tôi cũng muốn giải thích về cách nó hoạt động. Đây là câu hỏi đầu tiên về nó, vì vậy hãy làm cho nó một câu trả lời tốt!
Raynos

2
có một số lợi ích chính: có thể trừu tượng hóa kết quả của một tác vụ không đồng bộ có thể xảy ra, khả năng liên kết nhiều trình xử lý thuộc các loại khác nhau, liên kết trình xử lý với một tác vụ ngay cả sau khi tác vụ đã được giải quyết, ràng buộc kết quả của nhiều yêu cầu không đồng bộ cùng nhau, thêm trình xử lý có điều kiện, v.v.
ehynds

13

Thay vì cho bạn biết nó làm gì, tôi sẽ chỉ cho bạn nó làm gì và giải thích nó.

Bản sao của nguồn liên quan của jQuery 1.5 với chú thích giải thích những gì nó đang làm. Tôi nghĩ rằng các ý kiến ​​hầu hết là đúng.

Điều này có thể có lợi

// 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;
},

6
Điều này sẽ đọc đẹp hơn rất nhiều nếu bạn không có thanh cuộn ngang: /
gnarf

@gnarf Problem solved. Btw đó là nguồn 1.5beta Tôi nghĩ rằng có một số thay đổi trong 1.6
Raynos

9

Hãy sửa cho tôi nếu tôi sai, nhưng gần đây nó đã cho tôi biết rằng nó về cơ bản là một Trình chạy tác vụ không đồng bộ. Lời hứa là một hợp đồng kết quả, đảm bảo bạn nhận được ... thứ gì đó, nhưng không đảm bảo khi nào bạn sẽ nhận được nó.


Vì vậy, bình mới rượu cũ!
ankush981,

3

Trong khi làm việc trong Javascript, chúng tôi gặp phải trường hợp các lệnh gọi hàm không đồng bộ. Đó là luồng của hàm calee (giả sử X) không đợi hàm không đồng bộ được gọi (Giả sử Y). Ví dụ điển hình là khi chúng tôi thực hiện cuộc gọi đến máy chủ để tìm nạp một số dữ liệu từ cơ sở dữ liệu hoặc trang HTML. Nếu những cuộc gọi đó không đồng bộ, giao diện người dùng sẽ bị kẹt khi chờ máy chủ phản hồi. Tính chất không đồng bộ này dẫn đến một vấn đề khi bạn muốn thực thi mọi thứ theo một thứ tự, ví dụ, bạn muốn in thứ gì đó sau khi Y (asynch) thực hiện xong hoặc thực hiện xong việc tìm nạp dữ liệu. Ở đây jQuery cung cấp cho chúng ta Deffered Object. Về cơ bản, jQuery đã xử lý tất cả mã soạn sẵn mà chúng ta thường viết để giải quyết tình huống này. Đây là một ví dụ đơn giản:

  $.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

Bạn có thể viết hàm hoãn lại (không đồng bộ) của riêng mình

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
})

Tôi hy vọng điều này đã giúp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.