首頁 > web前端 > js教程 > 主體

jQuery的promise與deferred物件在非同步回呼中的作用_jquery

WBOY
發布: 2016-05-16 15:02:26
原創
1236 人瀏覽過

一、前言

為了讓前端們從回呼的地獄中回到天堂, jQuery 也引入了 Promise 的概念。 Promise 是一種令程式碼異步行為更優雅的抽象,有了它,我們就可以像寫同步程式碼一樣去寫非同步程式碼。 jQuery 從1.5版本開始實現了 CommonJS Promise/A 規範這個重量級方案,不過沒有嚴格按照規範進行實現,有一些API上的差異。

好,讓我們來看看他們的特性吧( 本文範例基於jquery 1.8版本以上 )。

二、範例

以前寫動畫時,我們通常是這麼幹的:

$('.animateEle').animate({
 opacity:'.5'
}, 4000,function(){
 $('.animateEle2').animate({
  width:'100px'
 },2000,function(){
  // 这样太伤了
  $('.animateEle3').animate({
   height:'0'
  },2000);
 });
});
登入後複製

假如這麼使用回調的話,那就太傷了。幸好,還有一些現成的 Promise 解決方案來優雅地解決這種問題。

我們來看看 jQuery 提供的解決方法。

var animate1 = function() {
 return $('.animateEle1').animate({opacity:'.5'},4000).promise();
};
var animate2 = function() {
 return $('.animateEle2').animate({width:'100px'},2000).promise();
};
var animate3 = function(){
 return $('.animateEle3').animate({height:'0'},2000).promise();
};
// so easy,有木有,so clear,有木有
$.when(animate1()).then(animate2).then(animate3);
登入後複製

很明顯,更改後的程式碼更易懂易讀了。

但是,上面的程式碼,有些細節的東西並沒有透露,一不小心,就容易出錯,得不到我們想要的順序完成動畫的效果。以下讓我們來全面理解 jQuery 提供的 promise 和 deferred 物件的方法,看看到底如何使用。

三、promise和deffered物件方法

promise 物件其實就是 deferred 物件的特例,因為 promise 物件無法更改非同步狀態,而 deferred 物件可以。這點在他們的方法設計上,有著明顯的體現。

1.promise物件方法

通常,對於DOM,動畫,ajax相關方法,我們都可以使用 promise 方法。呼叫 promise 方法,傳回的是 promise 物件。可以鍊式呼叫 promise 方法。

promise物件常見的方法有三個 : done , fail , then 。

其它的方法就不要去記了, jquery 這裡的接口方法太多了,在我看來挺囉嗦的,就跟早期的事件方法綁定一樣, live , delegate , bind ,最終不是都歸為on 來管了麼。

程式碼範例,如下:

(1)DOM使用 promise 方法:

var box=$('#box');
box.promise().done(function(ele){
  console.log(ele);//jQuery box
});
登入後複製

(2)Ajax使用 promise 方法(預設回傳一個 promise 對象,所以可以不必明確呼叫 promise 方法):

$.post('/',{}).done(function(data){
  console.log('请求成功');
}).fail(function(){
  console.log('请求错误');
});
登入後複製

動畫範例已有,就不重複列出了。

2.deferred物件方法

那麼Deferred和Promise之間有什麼差別呢?如你在前面看到的,一個promise就是一個由非同步函數傳回的物件。當你想要自己寫一個這樣的函數時你需要使用一個deferred。

一個deferred物件能做的和一個promise物件差不多,但是它有兩個函數來觸發done()和fail()函數。

一個deferred物件擁有一個resolve()函數來處理一個成功的結果並執行與done()相關的函數。 reject()函數則用來處理失敗的結果並執行與fail()相關的函數。

你可以給resolve()和reject()函數都提供參數,然後它們都會傳遞給與done()和fail()相關的回呼函數。

promise物件沒有resolve()和reject()函數。這是因為你將promise放到了其他的腳本中並且你也不想promise去resolve或reject一個promise。

下面是一個關於deferred的簡單例子。 html只是一個簡單的擁有id屬性為”result”的空div。

$('#result').html('waiting...');

var promise = wait();
promise.done(result);

function result() {
 $('#result').html('done');
}

function wait() {
 var deferred = $.Deferred();

 setTimeout(function() {
  deferred.resolve();
 }, 2000);

 return deferred.promise();
}  

登入後複製

其中,wait()函數傳回了一個promise。它將在2s之後被解析。除了setTimeout之外,非同步函數中所有的東西都能這樣使用,例如 動畫,We​​b worker等等。 wait()函數中的程式碼應該很清晰,我們使用了deferred對象,但是我們傳回了一個限制的promise物件。

對於 deferred 物件呢,也就是使用 $.Deferred() 方法,以及 $.when() 等方法創造出來的對象,有如下的常用方法:

resolve , reject , notify ;
done , fail , progress ;
登入後複製

另外還有 promise 、 then 和 always 方法。

之所以這麼排版,是因為他們是對應的,也就是說: resolve 方法會觸發 done 的回調執行, reject 會觸發 fail 的回調, notify 會觸發 progress 的回調。

直接看程式碼:

var wait = function(ms) {
 var dtd = $.Deferred();
 setTimeout(dtd.resolve, ms);
 // setTimeout(dtd.reject, ms);
 // setTimeout(dtd.notify, ms);
 return dtd.promise(); //此处也可以直接返回dtd
};

wait(2500).done(function() {
 console.log('haha,师太,你可让老衲久等了');
}).fail(function() {
 console.log('失败了');
}).progress(function(res) {
 console.log('等待中...');
});

登入後複製

我们看到了,上面的代码中,在 wait 函数中,返回的是个 promise 对象,而不是 deferred 对象。

要知道, promise 对象是没有 resolve , reject , notify 等方法的,也就意味着,你无法针对 promise 对象进行状态更改,只能在 done 或 fail 中进行回调配置。所以,你如果这么调用 wait(2500).resolve() 将会报错,因为 wait(2500) 返回的是个 promise 对象,不存在 resolve 方法。

但是,这么做,有个好处,我们把 dtd 这个 deferred 对象放在了 wai t函数中,作为了局部变量,避免了全局的污染;进一步通过 promise 方法,转化 dtd 这个 deferred 对象为 promise 对象,避免了函数 wait 外部可能发生的状态更改(假如我们确实有这个需求)。

比如:

var wait = function(ms) {
 var dtd = $.Deferred();
 setTimeout(dtd.resolve, ms);
 // setTimeout(dtd.reject, ms);
 // setTimeout(dtd.notify, ms);
 return dtd; //此处也可以直接返回dtd
};

wait(2500).reject().fail(function(){
 console.log('失败了...............');
});

登入後複製

我们在外部更改了 wait 返回的 deferred 对象的状态,这样必然触发该对象的 fail 回调函数。

对于 always 方法,从字面意思上就很容易理解, deferred 对象无论是 resolve 还是 reject ,都会触发该方法的回调。

3.其它共性

此处讲讲 then 和 $.when 方法的使用。它们对 promise 对象也适用。

$.when 方法接受多个 deferred 对象或者纯javascript对象,返回 promise 对象。
then 方法依次接受三个回调,分别为 deferred 对象 resolve , reject , notify 后触发的回调,返回一个 promise 对象。注意,必须传入函数,而该函数只有返回一个 promise 对象,才能够让异步事件按照预期顺序来执行。
我们来看看最开始的动画示例代码, $.when(animate1()).then(animate2).then(animate3) , $.when 方法中接受了一个 animate1 的函数执行结果,也就是得到了一个 promise 对象,而后的 then 中,则只是接受了一个变量名,这样得到的结果是一个匿名的函数体,而该函数中返回的是 promise 对象。正好符合了我们对 then 接受参数的要求。

假如我们把执行语句改成: $.when(animate1()).then(animate2()).then(animate3()) ,这样造成的结果就是三个动画同步执行了。与 $.when(animate1(),animate2(),animate3()) 无异。

既然 then 是如此要求,那么与 then 方法类似的 done , fail , progress 也是一样的。

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板