jquery的promise

蚊子前端博客
发布于 2015-04-18 06:00
为了让前端们从回调的地狱中回到天堂,jQuery也引入了Promise的概念。Promise是一种令代码异步行为更加优雅的抽象,有了它,我们就可以像写同步代码一样去写异步代码。jQuery从1.5版本开始实现了CommonJS Promise/A规范这一重量级方案,不过没有严格按照规范进行实现,有一些API上的差异。

为了让前端们从回调的地狱中回到天堂,jQuery也引入了Promise的概念。Promise是一种令代码异步行为更加优雅的抽象,有了它,我们就可以像写同步代码一样去写异步代码。jQuery从1.5版本开始实现了CommonJS Promise/A规范这一重量级方案,不过没有严格按照规范进行实现,有一些API上的差异。

1. 以前的ajax

1.1 原生的ajax写法

以前我们写ajax请求时,如果是用原生的JavaScript代码写呢,通常是这样的一种形式:

COPYJAVASCRIPT

var XHR = (function(){ var obj = null; // xhr对象 function init(){ create(); send(); } // 创建xhr对象 function create(){ if(window.XMLHttpRequest){ obj = new XMLHttpRequest(); }else if(window.ActiveObject) { obj = new ActiveObject('Microsoft.XMLHTTP'); } echange(); } // 发送消息 function send(){ var v = parseInt(Math.random()*100); obj.open("get", "test.php?v="+v, true); obj.send(null); obj.onreadystatechange =echange; } // 接收消息 function echange(){ /* 0 (未初始化) 对象已建立,但是尚未初始化(尚未调用open方法) 1 (初始化) 对象已建立,尚未调用send方法 2 (发送数据) send方法已调用,但是当前的状态及http头未知 3 (数据传送中) 已接收部分数据,因为响应及http头不全,这时通过responseBody和responseText获取部分数据会出现错误 4 (完成) 数据接收完毕,此时可以通过通过responseBody和responseText获取完整的回应数据 */ if(obj.readyState==4 && obj.status==200){ var result = JSON.parse(obj.responseText); console.log(result); } } return { init:init } })();

1.2 jquery的ajax写法

原生的ajax代码能比较清晰的看出ajax的流程,可是如果每次都写这么多的代码就会造成很多的不便。不过在jquery中,已经对ajax的代码进行封装,使用jquery的ajax时就不用写这么多的代码了。当然,我们自己也可以对原生的ajax代码进行封装。

COPYJAVASCRIPT

$.ajax({ url : 'test.php', // 请求地址 data : {'v':v}, // 发送的数据 dataType : 'json',// 返回的数据格式 type : 'get', // 请求的方式:get|post // 发送前执行 beforeSend : function(){ console.log("beforeSend"); }, // 返回成功执行 success : function(result){ console.log(result); }, // 返回失败执行 error : function(err){ console.log(err); }, // 无论成功还是失败都会执行 complete : function(){ console.log('complete'); }, // 状态码对应的操作 statusCode : { 200 : function(){ console.log("200, ok"); }, 404 : function(){ console.log("404, page not found"); } } });

上面的代码中虽然列了很多的选项,但是不是所有的都能用到的,可以结合实际的例子进行相应的取舍。比如beforeSend, statusCode等,如果实在是用不到,也可以不写的。

1.3 多个ajax顺序请求时

在使用这样的ajax进行多次顺序请求时,我们会采用嵌套的方式来进行。

COPYJAVASCRIPT

// 首次ajax请求 $.ajax({ url:'test.php', success:function(){ // 请求完成后进行下次的请求 $.ajax({ url:'test1.php', success:function(){ // 进行下次的ajax请求 $.ajax({ url:'test.php', success:function(){ // ... } }) } }) } })

如果有比较多的ajax请求时,我们发现这样的代码实在是不利于我们的阅读,而且造成以后代码的修改和维护很困难。那有没有一种写法,我们既能分开写ajax请求,又能顺序执行。

当然有啦,一个比较简单的方式就是使用异步回调的方式:

COPYJAVASCRIPT

// 第一个ajax请求 function ajax(callback){ $.ajax({ url:'test.php', success:function(){ callback(); } }) } // 第二个ajax请求 function ajax1(callback){ $.ajax({ url:'test.php', success:function(){ callback(); } }) } // 将第二个ajax方法作为第一个方法的回调方法 // 如果有多个ajax请求时,可以调用多个回调方法 ajax(ajax1); // ajax(ajax1(ajax2));

2. 现在的ajax

Promise/Deferred模式在JavaScript框架中最早出现于Dojo的代码中,被广为所知则来自于jQuery 1.5版本,该版本几乎重写了ajax部分,使得调用ajax时可以通过如下的形式进行:

COPYJAVASCRIPT

$.ajax({ url:'test.php' }) .success(function(){ }) .error(function(){ }) .complete(function(){ })

这使得即使不调用success(), error()等方法,ajax也会执行,这样的调用方式比预先传入的回调让人觉得舒适一些。在原始的API中,一个事件只能处理一个回调,而通过Deferred对象,可以对事件加入任意的业务处理逻辑,示例代码如下:

COPYJAVASCRIPT

$.ajax({ url:'test.php' }) .success(function(){ }) .error(function(){ }) .success(function(){ })

Promise/Deferred模式在2009年时被Kris Zyp抽象为一个提议草案,发布在CommonJS规范中,随着使用Promise/Deferred模式的应用逐渐增多,CommonJS草案目前已经抽象出了Promise/A、 Promise/B、Promise/D这样典型的异步Promise/Deferred模型,这使得异步操作可以以一种优雅的方式出现。

jQuery的1.5版本之后,$.ajax()返回的就是promise对象,而且其他的jQuery对象也能通过调用promise()返回一个promise对象。因此,上面的ajax可以写成这样:

COPYJAVASCRIPT

var promise = $.ajax({url:'test.php'}); promise.success(function(){ // success }) promise.error(function(){ // error })

不过在1.8版本之后,jquery已经不推荐success(),error()和complete()方法了,推荐使用done(), fail()和always(),分别对应着前面三个方法。修改如下:

COPYJAVASCRIPT

var promise = $.ajax({url:'test.php'}); promise.done(function(){ // done }) promise.fail(function(){ // fail }) promise.always(function(){ // always })

我们也可以用then()方法把done()和fail()合并到一起。

COPYJAVASCRIPT

promise.then(function(){ // done }, function(){ // fail })

第一个参数表示done方法,第二个方法表示fail方法;如果只传递一个参数的话,就表示done方法。

同时在jquery中还提供了$.when()方法

$.when(deferreds ) : 提供一种方法来执行一个或多个对象的回调函数, Deferred(延迟)对象通常表示异步事件

如果向 jQuery.when 传入一个延迟对象,那么会返回它的 Promise 对象(延迟方法的一个子集)。可以继续绑定 Promise 对象的其它方法,例如, defered.then 。当延迟对象已经被受理(resolved)或被拒绝(rejected)(通常是由创建延迟对象的最初代码执行的),那么就会调用适当的回调函数。

COPYJAVASCRIPT

$.when( $.ajax({url:'test.php'}) ).then(function(data, status, jqXHR){ // done // console.log(data, status, jqXHR); })

在案例中有多个延迟对象传递给jQuery.when ,该方法返回一个新的“宿主”延迟对象,跟踪所有已通过Deferreds聚集状态。 当所有的延迟对象被受理(resolve)时,该方法才会受理它的 master 延迟对象。当其中有一个延迟对象被拒绝(rejected)时,该方法就会拒绝它的 master 延迟对象。如果 master 延迟对象被受理(resolved),那么会传入所有延迟对象的受理(resolved)值,这些延迟对象指的就是传给 jQuery.when 的参数。例如,当延迟对象是 jQuery.ajax() 请求,那么传入的受理(resolved)参数就是请求用的 jqXHR 对象,传入顺序就是它们在参数列表中的顺序。

在多延迟情况下,如果延迟一被拒绝,jQuery.when触发立即调用 master 延迟对象的 failCallbacks。请注意,在上述情况中,有一些延迟对象依然是不能被受理(unresolved)的。那么,在这种情况下,如果需要执行一些额外的处理,例如,取消所有未完成的 ajax 请求,你可以在闭包中进行保持 jqXHR 对象的引用,并且在 failCallback 中检查或取消它们。

COPYJAVASCRIPT

$.when( $.ajax({url:'test.php'}), $.ajax({url:'test1.php'}) ).done(function(result, result1){ console.log(result); console.log(result1); }).fail(function(err){ console.log("error"); })

当两个ajax都执行成功时就会调用done方法,否则只要其中一个失败就会执行fail方法。

promise对象下可调用的方法有:

image-蚊子的前端博客

3. promise对象使用的其他场景

promise对象不只是能在ajax中使用,在其他有时间延迟的地方也能够使用,比如animate(), fadeIn(), fadeOut() 等,我们想在fadeIn()之后执行animate(),然后执行fadeOut让他隐藏。

一种方法就是使用这个方法提供的回调函数,来确保它的先后顺序:

COPYHTML

<style type="text/css"> .process{background: #ccc; display: none; width: 150px; height: 150px;} </style> <body> <div class="process"></div> </body> <script> // 记得加载jquery库 $('.process').fadeIn(function(){ $(this).animate({'width':'300px', 'height':'300px'}, 1000, function(){ $(this).fadeOut(); }) }) </script>

还有就是用上面讲到的promise对象。

COPYJAVASCRIPT

$('.process').fadeIn().promise() .then(function(){ $(this).animate({'width':'300px', 'height':'300px'}, 1000) }).then(function(){ $(this).fadeOut(); });
阅读(1174) 评论(0)

公众号:

qrcode

微信公众号:前端小茶馆