一、Promise的作用
在ajax請求數據的過程中,我們可以異步拿到我們想要的數據,然后在回調中做相應的數據處理。
這樣做看上去並沒有什么麻煩,但是如果這個時候,我們還需要做另外一個ajax請求,這個新的ajax請求的其中一個參數,得從上一個ajax請求中獲取,這個時候我們就需要在回調函數中再寫一個異步請求,然后在這個異步函數的回調函數里在寫相應的數據處理。要是連續嵌套個三四層,往往就很惡心了。
寫起來就像下面這樣:
$.ajax({
type:'get',
url:'url_1',
data: 'data'
success : function(res){
//相應的數據處理
var data = res.data
$.ajax({
type:'get',
url:'url_2',
data: data
success : function(res){
//相應的數據處理
$.ajax({
type:'get',
url:'url_3',
data: data
success : function(res){
//相應的數據處理
}
})
}
})
}
})
在這種情況下Promise就能發揮它的威力了;
二、來一個實例
先不談語法,下面先來一個實例,建立感性的認識
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.js"></script>
</head>
<body>
</body>
<script>
function a(data){
return new Promise(function(resolve,reject){
console.log("我是從上一個回調函數里傳過來的數據",data) ;
$.ajax({
type:'post',
dataType: 'jsonp',
url:'http://api.money.126.net/data/feed/0000001,1399001', //jsonp跨域調用上證與深證的股票指數
data:{
},
success : function(res){
console.log(res) ;
resolve(res) ;
},
error:function(res){
console.log("Error:") ;
console.log(res) ;
reject(res) ;
}
})
});
}
function b(data){
return new Promise(function(resolve,reject){
console.log("我是從上一個回調函數里傳過來的數據",data) ;
$.ajax({
type:'post',
dataType: 'jsonp',
url:'https://api.douban.com/v2/movie/top250', //跨域調用豆top250的電影
success : function(res){
console.log(res) ;
resolve(res) ;
},
error:function(res){
console.log("Error:") ;
console.log(res) ;
reject(res)
}
})
});
}
a().then(b).then(a).then(b).catch(function(a){console.log("final Error:",a)}) ;
</script>
</html>
打印結果如下所示:
可以發現,
Promise
通過簡單的鏈式調用就能得到之前多層回調才能達成的效果;而且從代碼的結構來看,有效地減小了各個請求之間的耦合;
三、深入Promise
別的不談,先打印一下 Promise
, console.dir(Promise)
, 看看它究竟是哪號人物:
原來
Promise
本身是一個構造函數,自己身上有all
、reject
、resolve
這幾個的方法,在其prototype
上有then
、catch
這兩個方法。那么用Promise new出來的對象也會有then
、catch
這兩個方法。
四、注意上面實例中的resolve與reject
1、我們發現,在 new Promise(function(resolve,reject){})
里傳了兩個方法 resolve
、 reject
作為參數,這兩個方法通常會在函數的回調里被用到。一旦執行到resolve()
或者 reject()
,那么這個函數會停止執行,然后觸發后面的 then()
或者 catch()
方法。准確一點來說,執行到resolve()
會觸發 then()
方法,執行到 reject()
會觸發 catch()
方法。
2、resolve
和 reject
方法里可以傳入參數 ,就像 resolve(data)
和 reject(data)
。 如果這樣做 ,那么在后面的 then()
或者 catch()
里傳入一個帶參數的函數 , 就像 then(function(data){})
或者 catch(function(data){})
, 就能得到 data
的數據 。
3、說的再專業一些,Promise
對象有三種狀態,他們分別是:
- pending: 等待中,或者進行中,表示還沒有得到結果
- resolved(Fulfilled): 已經完成,表示得到了我們想要的結果,可以繼續往下執行
- rejected: 也表示得到結果,但是由於結果並非我們所願,因此拒絕執行
這三種狀態不受外界影響,而且狀態只能從 pending
改變為 resolved
或者rejected
,並且不可逆。在 Promise
對象的構造函數中,resolve
和 reject
就是用來處理Promise的狀態變化。
一般來說,調用 resolve
或 reject
以后,Promise 的使命就完成了,后繼操作應該放到 then
或者 catch
方法里面,而不應該直接寫在 resolve()
或 reject()
的后面 (事實的情況是,resolve()
或 reject()
的后面的代碼也不會執行)。
五、new Promise()
里的函數是立刻執行的
需要注意的的是,new Promise()
里的函數是立刻執行的 ,也就是說 ,當你執行下面這段代碼時,就已經開始執行異步請求了:
<script>
new Promise(function(resolve,reject){
$.ajax({
type:'post',
dataType: 'jsonp',
url:'http://api.money.126.net/data/feed/0000001,1399001',
data:{
},
success : function(res){
console.log(res) ;
resolve(res) ;
},
error:function(res){
reject(res) ;
}
})
});
</script>
這也是為什么,在上面第二段的實例中,需要用 a()
和 b()
函數把 new Promise()
給包起來
六、then()
函數的返回值一定是 Promise
對象
還需要注意的的是,then()
函數的返回值一定是 Promise
對象,哪怕手動 return
一個值也無濟於事,如下面的代碼,照樣能運行成功:
a().then(function (){console.log("hello");return 1}).then(b) ;
這也解釋了為什么我們可以鏈式調用 then()
函數。
七、Promise.all()
與Promise.race()
的用法
想要從兩個不同的 ajax
請求里分別獲得信息,這兩個任務是可以並行執行的,就可以用 Promise.all()
實現:
<script>
var p1 = function(){
return new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
} ;
var p2 = function(){
return new Promise(function (resolve, reject) {
setTimeout(resolve, 1000, 'P2');
});
} ;
// 同時執行p1和p2,並在它們都完成后執行then
var start = function(){
Promise.all([p1(), p2()]).then(function (results) {
console.log(results); // 獲得一個Array: ['P1', 'P2']
});
}
</script>
有些時候,多個異步任務是為了容錯。比如,分別發兩個不同的 ajax
請求讀取用戶的個人信息,只需要獲得先返回的結果即可,這種情況下,就可以用Promise.race()
實現:
<script>
var p1 = function(){
return new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
} ;
var p2 = function(){
return new Promise(function (resolve, reject) {
setTimeout(resolve, 1000, 'P2');
});
} ;
var start = function(){
Promise.all([p1(), p2()]).then(function (results) {
console.log(results); // 'P1'
});
}
</script>
由於 p1
執行較快,Promise
的 then()
將獲得結果 'P1'
。 p2
仍在繼續執行,但執行結果將被丟棄。
如果我們組合使用Promise,就可以把很多異步任務以並行和串行的方式組合起來執行。