JQuery deferred object

Time:2021-2-25

What is deferred object

In the process of website development, we often encounter some time-consuming JavaScript operations. Among them, there are both asynchronous operations (such as Ajax reading server data) and synchronous operations (such as traversing a large array), none of which can get results immediately.

The usual practice is to specify callback functions for them. That is, it specifies in advance which functions should be called once they are finished.

In short, deferred object is the callback function solution of jQuery. In English, defer means “delay”, so deferred object means “delay” until a certain point in the future.

It solves the problem of how to deal with time-consuming operations, provides better control over those operations, and provides a unified programming interface. Its main functions can be summed up in four points.

The chain style of Ajax

Let’s look at an example of a $. Ajax request:

$.ajax({
    url: 'test.jspx',
    success: function(data) {
        //The request was successful
        // TODO
    },
    error: function(error) {
        //The request failed
        // TODO
    }
});

The above code is very common, but you must have seen the following writing:

$.ajax('test.jspx')
    .done(function(data) {
        //The request was successful
        // TODO
    })
    .fail(function(error) {
        //The request failed
        // TODO
    });
    
$.ajax({
    url: 'test.jspx',
    type:'POST',
    data: data
}).done(function(data) {
    //The request was successful
    // TODO
}).fail(function(error) {
    //The request failed
    // TODO
});

In fact, jQuery before version 1.5.0 does not support chain writing, and can only use the first writing method, because the Ajax operation of jQuery before this version returns aXHR (XMLHttpRequest) Object. This object doesn’t look likedoneandfailThis is the callback method.

Later versions return a deferred object withpromiseMethods wrapped objects can be chained. After chaining, the readability of the code is greatly improved.

Specify multiple callback functions for an operation

What if you want to execute another callback function after an Ajax request succeeds? You can just add it directly after:

$.ajax('test.jspx')
    .done(function(data) {
        //The request was successful
        // TODO
    }).fail(function(error) {
        //The request failed
        // TODO
    }).done(function(data) {
        //The request was successful
        // then TODO
    });

This writing method can support countless callback functions, which will be executed in the order of addition.

Specifying callback functions for multiple operations

What if a callback function needs to be executed after several Ajax requests are successful? Is it hard to control? In fact, jQuery provides such a method$.when()It can receive any deferred object, and only execute when all deferred objects are successfuldoneCallback, otherwise executefailCallback.

$.when($.ajax("test1.html"), $.ajax("test2.html"))
    .done(function() {
        //Both requests were successful
        // TODO
    }).fail(function() {
        //Any one fails
        // TODO
    });

In the above code, only two requests succeed before executiondoneCallbacks are executed as long as one failsfailCallback.

Specify callback functions for normal operations

The biggest advantage of deferred object is that it extends this set of callback function interfaces from Ajax operations to all operations. In other words, any operation, whether Ajax or local, asynchronous or synchronous, can use various methods of the deferred object to specify the callback function.

var wait = function() {
    Var DTD = $. Deferred(); // create a deferred object
        
    var tasks = function() {      
        Alert ("execution complete! ");      
        dtd.resolve (); // after this operation, the execution state of the deferred object will be changed                          
    };      
    setTimeout(tasks, 5000);    
    return dtd;  
};

//Binding callback function                 
$.when(wait())
    .done(function() {
        Alert ("execution succeeded! ");
    })
    .fail(function() {
        Alert ("wrong! ");
    });

In the above codewaitMethod simulates a time-consuming operation, and then specifies a callback function for the operationdoneandfail. oncewaitIt will be called immediately after executiondoneThis callback function.

Common methods of deferred object

It’s right up theredeferredNow that we know something about the object, let’s introduce itdeferredObject.

deferred.resolve()

In jquerydeferredObject, it has three states

hang in the airKeep waiting

CompletedCall the done callback immediately

FailedCall the fail callback immediately

resolveThe purpose of the method is to set thedeferredWhen the object state is completed, the deferred object immediately calls the callback function specified by the done () method

deferred.reject()

rejectMethod is used to set thedeferredIf the object state is failed, the deferred object immediately calls the callback function specified by the fail() method

deferred.done()

doneMethod to specify thedeferredCallback function when the object state is completed.

deferred.fail()

doneMethod to specify thedeferredCallback function when object status is failed.

deferred.then()

thenMethod receives one to three parameters, which are specified separatelydeferredCallback function with object status of succeeded, failed, and continue waiting.

deferred.always()

alwaysMethod to specify thedeferredCallback function when the object state is successful or failed.

That is, regardless of thisdeferredThe callback specified by this method will be called as long as the object is successful or failed.

Because the parameters of the callback function specified by this method are uncertain (for example, the information returned by successful and failed Ajax requests is different, the data returned when successful and the error information when failed), it is better to use only its behavior without checking its parameters. If the operation to be performed is related to the parameter, specify it explicitlydoneandfailCallback. As follows:

$.ajax('test1.html')
    .always(function() {
        //Whether the request is successful or not, it is executed as long as the request is completed
        // TODO
        console.log ('request completed, status unknown ');
    });

$.ajax('test1.html')
    .done(function(data) {
        //Execute on request success
        // TODO
        console.log ('request succeeded, data returned: ');
        console.log(data);
    })
    .fail(function(error) {
        //Execute on request failure
        // TODO
        console.log ('request failed, error message: ');
        console.log(error.status, error.statusText);
    });

deferred.progress()

progressMethod to specify thedeferredThe object state is the callback function in wait. But it’s only in thedeferredObject is called only when it generates a progress notification.

Here’s an example:

var wait = function() {
    Var DTD = $. Deferred(); // create a deferred object
        
    var tasks = function() {      
        Alert ("execution complete! ");      
        dtd.resolve (); // after this operation, the execution state of the deferred object will be changed                           
    };    
    setTimeout(tasks, 5000);    
    return dtd;  
};

//Binding callback function                 
$.when(wait())
    .done(function() {
        Alert ("execution succeeded! ");
    })
    .fail(function() {
        Alert ("wrong! ");
    })
    .progress(function(){
        console.log ("executing..."); // there will be no output here
    });

Although the progress callback is specified above, it has no effect because thedeferredObject does not generate a progress notification, so it will not be called.

If you want the progress callback to be executed, you need todeferredObject.notifyMethod is to call the progress callback in progress on the deferred object according to the given args parameter.

var wait = function() {
    Var DTD = $. Deferred(); // create a deferred object    
    var i = 1,
        timer,
        Percentage; // record progress


    var tasks = function() {
        if (i == 11) {
            Alert ("execution complete! ");
            dtd.resolve (); // after this operation, the execution state of the deferred object will be changed
        } else {
            percent = (i * 500) / 5000 * 100 + '%';
            dtd.notify (percentage); // call the progress callback
            i++;
            setTimeout(tasks, 500);
        }
    };
    setTimeout(tasks, 1000);
    return dtd;
};
//Binding callback function
$.when(wait())
    .done(function() {
        Alert ("execution succeeded! ");
    })
    .fail(function() {
        Alert ("wrong! ");
    })
    .progress(function(data) {
        console.log ('executing, completed ', data));
    });
//Implementation, 10% completed
//In progress, 20% completed
//Implementation, 30% completed
//Implementation, 40% completed
//In progress, 50% completed
//Under implementation, 60% completed
//Implementation, 70% completed
//Implementation, 80% completed
//In progress, 90% completed
//Implementation, 100% completed
//After that, the pop-up is completed! And the implementation is successful!

This method provides a possibility to upload files or generate progress bar by time-consuming operation.

JQuery version 3.0 or abovewhenThe method has been greatly adjusted. towardspromise/A+It’s in the above waynotifyCan’t trigger the progress callback in when, you need to usepromiseTo deploy the deferred interface to an object or use the$.Deferred()Pass in the function name.

In short, in version 3.0 or above, the progress callback in the above code will not go in. You should use the following writing method:

1、promiseTo deploy an objectDeferredInterface:

Var DTD = $. Deferred(); // create a deferred object
var wait = function(dtd) {
    var i = 1,
        timer,
        Percentage; // record progress
    var tasks = function() {
        if (i == 11) {
            Alert ("execution complete! ");
            dtd.resolve (); // after this operation, the execution state of the deferred object will be changed
        } else {
            percent = (i * 500) / 5000 * 100 + '%';
            dtd.notify (percentage); // call the progress callback
            i++;
            setTimeout(tasks, 500);
        }
    };
    setTimeout(tasks, 1000);        
};
//After deploying the deferred interface on the wait object, you can directly use the method after the deferred object promise on the wait
dtd.promise(wait);
//On the wait object, use the method of the deferred object to specify the callback.
wait.done(function() {
    Alert ("execution succeeded! ");
})
.fail(function() {
    Alert ("wrong! ");
})
.progress(function(data) {
    console.log ('executing, completed ', data));
});
//Implementation
wait(dtd);

2. Use$.DeferredPass in function name:

var wait = function(dtd) {
    var i = 1,
        timer,
        Percentage; // record progress

    var tasks = function() {
        if (i == 11) {
            Alert ("execution complete! ");
            dtd.resolve (); // after this operation, the execution state of the deferred object will be changed
        } else {
            percent = (i * 500) / 5000 * 100 + '%';
            dtd.notify (percentage); // call the progress callback
            i++;
            setTimeout(tasks, 500);
        }
    };
    setTimeout(tasks, 1000);
    return dtd;
};    
//Binding callback function
$.Deferred(wait)
    .done(function() {
        Alert ("execution succeeded! ");
    })
    .fail(function() {
        Alert ("wrong! ");
    })
    .progress(function(data) {
        console.log ('executing, completed ', data));
    });

deferred.promise()

promiseThe function of the method is to return another deferred object on the original deferred object. The latter only opens methods unrelated to changing the execution state (such as done() method and fail() method), and shields methods related to changing the execution state (such as resolve() method and reject() method), so that the execution state cannot be changed.

Var DTD = $. Deferred(); // create a deferred object
          
        var wait = function(dtd) {    
            var tasks = function() {      
                Alert ("execution complete! ");      
                dtd.resolve (); // change the execution state of deferred object            
            };    
            setTimeout(tasks, 5000);    
            return dtd;  
        };  
        $.when(wait(dtd))  
            .done(function() {
                Alert ("execution successful! ");
            })  
            .fail(function() {
                Alert ("wrong! ");
            });
        dtd.reject (); // if you change the status to fail, you will immediately trigger fail and start again after 5 seconds

If we define the deferred object outside the function, setting the state of the deferred object will cause the corresponding callback to be called. Last call in the above coderejectMethod, the fail callback will be called immediately, and the execution will be completed and executed successfully after 5 seconds. This will lead to unnecessary confusion. usepromiseThe method is a solution. (previously written willvar dtd = $.Deferred()It’s also a solution to put it inside the function so that it can’t be accessed from outside.

Var DTD = $. Deferred(); // create a deferred object
  
var wait = function(dtd) {    
    var tasks = function() {
        Alert ("execution complete! ");      
        dtd.resolve (); // change the execution state of deferred object
    };    
    setTimeout(tasks, 5000);    
    return  dtd.promise (); // returns the promise object
      
};  
Var d = wait (DTD); // create a new D object and operate on it instead
  
$.when(d)
    .done(function() {
        "Ha ha, it's a success! ");
    }).fail(function() {
        Alert ("wrong! ");
    });  
d. Resolve(); // d.resolve is not a function. There is no resolve method after promise

Let’s take a lookDeferredObject and itpromiseThe difference after that.

JQuery deferred object

promiseMethods related to changing state have been removed from the returned object.notifyandnotifyWithCall the progress callback,resolveandrejectUsed to set its state. The method with with with can specify the context.

This method can also receive parameters of type object,deferred.promise()Instead of creating a new object, the event is bound to the parameter and the object is returned. This method can be used to bind promise behavior on existing objects. Examples are as follows:

Var DTD = $. Deferred(); // generate deferred object
  
var wait = function(dtd) {    
    var tasks = function() {      
        Alert ("execution complete! ");      
        dtd.resolve (); // change the execution state of deferred object after execution
            
    };    
    setTimeout(tasks, 5000);  
};   
//After deploying the deferred interface on the wait object, you can directly use the method after the deferred object promise on the wait
dtd.promise(wait);
//On the wait object, use the method of the deferred object to specify the callback.
wait.done(function() {
    "Ha ha, it's a success! ");
})  .fail(function() {
    Alert ("wrong! ");
});  
//Implementation
wait(dtd);

$.Deferred()

$.Deferred()In addition to creating a deferred object, you can take a function name (note, the function name) as an argument,$.Deferred()The generated deferred object will be used as the default parameter of this function.

var wait = function(dtd) {    
    var tasks = function() {      
        Alert ("execution complete! "); 
        dtd.resolve();         
    };    
    setTimeout(tasks, 5000);      
};

$.Deferred(wait)
    .done(function() {
        Alert ("execution successful! ");
    })
    .fail(function() {
        Alert ("execution failed! ");
    });

$.when()

Receives one or more deferred objects as parameters and specifies a callback function for them.

seeSpecifying callback functions for multiple operations

deferred.then The transmission function of

As we said before,thenMethod takes one to three parameters and specifies thedeferredCallback function with object status of succeeded, failed, and continue waiting.

besides,thenDelay objects can also be passed. We know,deferredThe reason it can be chained is that it returnsdeferredObject.thenIf the callback function in does not return a newdeferredObject, the original one will still be useddeferredObject, if it also returns adeferredObject, the subsequent operations will be transferred to the new return in the callback functiondeferredObject for passing.

Let’s take an example

There are five JS files, whose contents are as follows:

// perosn.js
var Person = function(name){
    this.name= name;
};

// person.prototype.js
if (Person) {
    Person.prototype.showName = function() {
        return this.name;
    };
} else {
    console.error('Person is undefined!');
}

// zs.js
Var ZS = new person ('zhang San ');

// ls.js
Var LS = new person ('li Si ');

// introduce.js
var introduceEachOther = function() {
    console.log (Zhang San: Hello, my name is+ zs.showName ());
    console.log (Li Si: Hello, my name is+ ls.showName ());
};

The contents of the files are very simple and are only used for demonstration, but the dependency is very obvious, person.prototype.js Depend on perosn.js , zs.js and ls.js Depend on person.js , introduce.js It depends on all other JS.

To load step by step and ensure availability, it is necessary to ensure that when loading the current JS, its dependent JS has been loaded.

Using the traditional writing method, it is bound to nest multiple layers, not only the logic is not easy to handle, but also the readability is poor.

We usethenShow me how to pass the deferred object to complete this operation.

$.ajax({
    url: 'person.js',
    dataType: 'script'
}).then(function() {
    console.log (Person); //  person.js It has been loaded successfully
    console.log (' person.js Loaded successfully '); 
    return $.ajax({
        url: 'person.prototype.js',
        dataType: 'script'
    });
}).then(function(data) {
    //The content of the last request file of data here is person.prototype.js Instead of person.js
    console.log(data);
    console.log(Person.prototype.showName);
    console.log (' person.prototype.js Loaded successfully ');
    //  person.prototype.js  Already loaded
    return $.when($.ajax({
        url: 'zs.js',
        dataType: 'script'
    }), $.ajax({
        url: 'ls.js',
        dataType: 'script'
    }));
}).then(function(){
    //  zs.js  And ls.js  All loaded
    console.log(zs,ls);
    console.log (' zs.js and zs.js Loaded successfully ');       
    return $.ajax({
        url: 'introduce.js',
        dataType: 'script'
    });
},function(){
    console.log('zs.js or ls.js failed');
}).then(function(){
    //All previous resources were loaded successfully
    console.log (' introduce.js It has been loaded successfully, and its dependent resources have been loaded successfully. It is ready to use ');       
    introduceEachOther();
});

Online address

The above processing sequence is just an example, as long as the guarantee person.js Load first, introduce.js Finally, it is loaded, person.prototype.js stay person.js After that. ls.js 、 zs.js And person.prototype.js The order can be adjusted.

Every time wethenA new deferred object is returned in the first callback of. The subsequent operation is based on the deferred object you returned. The secondthenThe data output in the callback proves this. (if not)returnIf a new deferred object is created, the previous one will still be used.)

Reference link:

This article is written according to the following article plus my own learning experience.

Ruan Yifeng: detailed explanation of deferred object of jquery

Recommended Today

Third party calls wechat payment interface

Step one: preparation 1. Wechat payment interface can only be called if the developer qualification has been authenticated on wechat open platform, so the first thing is to authenticate. It’s very simple, but wechat will charge 300 yuan for audit 2. Set payment directory Login wechat payment merchant platform( pay.weixin.qq . com) — > Product […]