My technical level is generally limited. If there are any mistakes, please correct me.
ES6 has already implemented the delayed object Promise, but today the protagonist is the delayed object in JQ, and the routines are actually the same. Let’s take a look at a far-fetched example:
<button id="add">add</button><button id="remove">remove</button> <div id="content"></div>
$(function(){ var dfd = new $.Deferred(); var add = $("#add"); var remove = $("#remove"); var content = $("#content"); add.click(function(){ var span = $("<span>我是点击按钮创建的元素</span>"); $("span").length==0&&$("body").append(span); dfd.resolve(); }) remove.click(function(){ var span = $("span"); span&&span.remove(); }) dfd.done(function(){ $("span").css("color","red"); }) })
Now focus on the function. When we click the add button, we can see that the span element is added and the color turns red. Then we looked at the heterogeneous things in our code, the beginning var dfd = new $.Deferred(); and the dfd.resolve() in the add event function; and the last dfd.done(function(){ $("span").css("color","red");}).
$.Deferred() is the focus of our introduction today---the delay object in JQ. The so-called delay means that it can run at a certain event in the future. Let's look at a processing flow of the above code. In the above code, we pass an anonymous function expression in the parameter position of the dfd.done() method of the newly created delayed object, and call dfd when the click event handler is executed. resolve(), and then the anonymous function we wrote in dfd.done() will be executed. In this process, we can see that dfd places the function in done in the resolve position. dfd is a delay object. Obviously it can change the execution order of functions.
When you look at the above code, if you think about it carefully, you will find that it is useful. We put the code for changing the color in a function, and we can just call this function when we click it. It is so troublesome to write. For birds. In fact, the most common use of deferred objects is in AJAX. In the above code, after we click add, we click remove and then click add. At this time, we find that the word does not turn red this time. This is because the delay object becomes invalid after the state changes. To put it bluntly, it is a one-time use.
Using deferred objects
JQ implements the function of deferred objects for us. We generally call it Deferred or Promise, which is basically the same thing. To be precise, Promise is a subclass derived from Deferred.
When we use it, we first create a deferred object: var dfd = new $.Deferred().
The delayed object dfd has three states: pending, resolved, and rejected. We can check the state at this time by using the state method on the dfd object: dfd.state().
After dfd is created, its status is pending. After calling the resolve method: dfd.resolve(), its status will change to resolved and then the function in dfd.done() will be executed. After dfd calls the reject method: dfd .reject() will change its status to rejected and then execute the method in dfd.fail(), and the dfd object will not change after changing from pending to resolved or rejected. This is what we have above The reason why the code is only red after the first click.
Let’s take a look at the starting code. Our dfd.done() defines a function that turns the font red. After clicking on the function to execute, dfd calls resolve. After that, the status of dfd changes from pending to resolved will execute the method in done and then the color will turn red.
Parameters can be passed between dfd.resolve() and dfd.done(). Now we make some modifications to the starting code:
//done里面的修改如下 dfd.done(function(color){$("span").css("color",color)}) //点击事件的处理函数修改如下 dfd.resolve("green");
The font color changes to green after we click.
In addition, dfd has another function always: dfd.always(). Whenever the status of dfd changes from pending to any state, the functions in always will be executed.
Every method of dfd will return a delay object, so there can be multiple done, fail, and always, which can be directly written as a chain call:
dfd.done(function(){}).done(function (){}).fail(function(){});
No matter which API of dfd, you can write multiple ones. At this time, we may consider whether its execution order can be guaranteed. We can rest assured that the order of execution of dfd's functions is absolutely no problem and will be executed in the order we wrote it. Look at the following code:
dfd.done(function(){ var span = $("<span>我是点击按钮创建的元素</span>"); $("span").length==0&&$("body").append(span); }) .done(function(color){ $("span").css("color",color)}); })
The first function adds elements, and the second function changes and adds The color of the element.
Whenever the functions in the three APIs of dfd can be executed only after the status of dfd changes from pending, this is the case in asynchronous cases and also in synchronous cases. To be more precise, the functions in done that have been executed after dfd calls dfd.resolve() will be executed immediately. For the done after dfd.resolve(), it will not be executed until the program reaches it:
var dfd = new $.Deferred(); dfd.done(function(){console.log(1)}); dfd.done(function(){console.log(2)}); console.log("resolve before"); dfd.resolve(); console.log("resolve after"); dfd.done(function(){console.log(3)}); //resolve before,1,2,resolve after,3
Delayed object example
When we first used JQ’s AJAX, our usual way of writing it was:
$.ajax({ url:"x/y", type:"post", data:"{...}", contentType:"application/json; charset=utf-8", success:function(){}, error:function(){} })
After 1.5 (it seems to be this version~) AJAX will return a Promise object, and then We can write it like this:
$.ajax({ url:"x/y", type:"post", data:"{...}", contentType:"application/json; charset=utf-8", }).done(function(){}) .fail(function(){})
It looks a little more irritating, and we can also add multiple .done(function(){}), each done handles different things like this It looks clearer.
已经知道延迟对象可以改变代码的执行顺序,假如我们又下面的代码:
$.ajax({ url:"取数据", type:"post", contentType:"xxx" }).done(function(data){ $.ajax({ url:"利用data取数据", data:data, type:"post", contentType:"xxxx" }).done(function(data){ use data to _do sth... }) })
我们会发现嵌套的有点多了,我们就可以利用延迟对象让他看起来更加好看一点:
var dfd = new $.Deferred(); $.ajax({ url:"取数据", type:"post", contentType:"xxx" }).done(function(data){ dfd.resolve(data); }) dfd.done(function(data){ $.ajax({ url:"利用data取数据", data:data, type:"post", contentType:"xxxx" }).done(function(data){ use data to _do sth... }) })
没有延迟对象我们一样能完成需要的功能,此时我们就需要一层一层嵌套我们处理过程了,而有了延迟对象我们就可以避免这种事了,他可以轻松控制代码的执行顺序,让代码看起来更请清晰。你可能会说我封装函数在合适的地方调不就行了,如果自己看自己写的代码没问题,但是换一个人他的滚动条可能就一直上上下下了。
延迟对象的里一个作用就是可以合并AJAX的调用,比如一个接口取数据A另一个接口取数据B,AB都取到之后我们在利用这些数据做一些喜欢做的事,我们就可以利用延迟对象轻松实现。此时我们就可以利用JQ的$.when()来实现。$.when()就跟他的名字一样-当什么的时候-,他的参数可以是Promise对象,也可以是字符串(很少遇到不在介绍),他的返回结果也是一个Promise对象,下面看一个小例子:
var allData = {}; var dataA = $.ajax({ url:"获取A的URL", type:"post", }).done(function(data){ allData.a = data; }); var dataB = $.ajax({ url:"获取B的URL", type:"post", }).done(function(data){ allData.b = data; }); $.when(dataA,dataB).done(function(){ use allData to _do sth... });
allData是保存所有数据的一个集合,dataA是第一个AJAX返回的Promise对象,dataB是第二个。$.when()的done方法执行的唯一条件就是dataA和dataB都执行成功。
补充:dfd还有一对组合就是notify+progress当dfd对象的状态处于pending时可以调用dfd.nothfy(),调用之后dfd.progress()里面的函数会执行,只要dfd处于pending状态dfd.notify()就可以一直调用,同样也可以传递参数。