JS里面有大量的异步方法,写着写着,代码就会变得>>。多层嵌套的回调,很影响后续代码的维护,也许今天你还记得这块回调逻辑,明天你就很有可能被这回调姿势给坑了。
那么,今天就介绍一种抹平回调的方法,jQuery.Deferred。
$.Deferred() 从字面上理解,就是一个延迟对象。它是jQuery出的,为了解决回调嵌套,方便开发者的一种函数。
好像好高深,其实我们很早就有接触,并经常在用到。
$.getJSON().done(function(){
alert('成功');
})
jQuery的$.ajax()本身就支持Deferred,它可以链式补上 .done() .fail() 等方法来处理不同状态的结果。
例如动态获取一张图片的宽度
/**
* 使用Deferred前
**/
var getImgWidth = function(){
var $img = $('img');
$img.on('load', function(){
alert($(this).width());
})
$img.attr('src', 'https://www.google.com/images/srpr/logo11w.png');
return $img.width();
}
当调用 getImgWidth() 的时候,其返回值没法获取到正确的宽度,而随后当图片加载完成触发load事件后,alert出来的宽度才是正常的。
那么,我们改怎么修改这个方法呢?
/**
* 使用Deferred后
**/
var getImgWidth = function(){
var dfd = $.Deferred();
var $img = $('img');
$img.on('load', function(){
dfd.resolve($(this).width());
})
$img.attr('src', 'https://www.google.com/images/srpr/logo11w.png');
return dfd.promise();
}
这段升级后的函数,再获取的宽度的时候,就可以使用如下方法轻松获得
getImgWidth().done(function(width){
alert('图片的宽度是:'+ width);
})
看完这段改造后的代码,也许有人会说,这个作用不大呀,和直接给getImgWidth(callback) 加多一个回调函数没什么区别。
嗯,从这里来看,貌似是这样,但是,当我们要同时处理多个异步的时候,$.Deffered()的威力就体现出来了。
比如,需求是同时加载完两张图片,得到两个宽度之后,才开始执行后续逻辑。
传统的回调要怎么写?反正我是晕了。
用$.Deferred()就很简单,getImgWidth()的定义不变,使用$.when()方法来处理同时完成的事件。
$.when(getImgWidth(), getImgWidth()).done(function(width1,width2){
alert('第一张图片宽度:'+ width1);
alert('第二章图片宽度:'+ width2);
});
怎样,有没发现突然晴空万里,以前的业务逻辑里面各种为了解决多个异步条件的setTimeout()是不是瞬间变得很没用。
拥抱Deferred,抛弃那些不可控的setTimeout()吧。
最后引用阮一峰的《jQuery的deferred对象详解》里面的小结吧: