Promise是異步里面的一種解決方案,解決了回調嵌套的問題,es6將其進行了語言標准,同意了用法,提供了`promise`對象, promise對象有三種狀態:pending(進行中) 、Resolved(已經完成)和Rejected(已失敗)
ES6規定,Promise對象是一個構造函數,用來生成Promise實例。
|
1
2
3
4
5
6
7
|
var
promise=
new
Promise(
function
(resove,reject){
if
(
/* 異步操作成功 */
){
resolve(value);
}
else
{
reject(error);
}
})
|
Promise實例生成以后,可以用then方法分別指定Resolved狀態和Reject狀態的回調函數。
|
1
2
3
4
5
|
promise.then(
function
(value) {
// success
},
function
(error) {
// failure
});
|
案例
|
1
2
3
4
5
6
7
|
var
promise=
new
Promise(
function
(resolve,reject){
console.log(
'promise'
);
})
promise.then(
function
(){
console.log(
'我后執行'
)
})
console.log(
'我先執行'
)<br><br>
//上面代碼中,Promise新建后立即執行,所以首先輸出的是“Promise”。然后,<code>then</code>方法指定的回調函數,<br>//將在當前腳本所有同步任務執行完才會執行,所以“我后執行” 最后輸出。
|
ajax
var
http = {
get:
function
(url) {
var
promise =
new
Promise(
function
(resolve, reject) {
$.ajax({
url: url,
method:
'get'
,
success:
function
(data) {
resolve(data);
},
error:
function
(xhr, statusText) {
reject(statusText);
}
});
});
return
promise;
}
};
http.get(
'solve.php'
).then(
function
(data) {
return
data;
},
function
(err) {
return
Promise.reject(
'Sorry, file not Found.'
);
}).then(
function
(data) {
document.write(data);
},
function
(err) {
document.write(err);
});
1 var getJSON=function(url){
2 var promise=new Promise(function(resolve,reject){
3 var xhr=new XMLHttpRequest();
4 xhr.open('get',url);
5 xhr.onreadystatechange=handler;
6 xhr.send(null);
7
8 function handler(){
9 if(this.readystate!==4){
10 return;
11 }
12 if(this.status==200){
13 resolve(this.responseText)
14 }else{
15 reject(new Error(this.statusText));
16 }
17 }
18 })
19 return promise;
20 }
21 getJSON("/get.json").then(function(json) {
22 console.log('Contents: ' + json);
23 }, function(error) {
24 console.error('出錯了', error);
25 });
Promise三種狀態
pending、resolved、rejected
使用語法
var promis = new Promise(function(resolve,reject){ $.ajax({ url:'/api/something.json', method:'get', datatype:'json', success:(res) =>{ resolve(res) }, error:(err)=>{ reject(err) } }) }) promis.then(function(res){ console.log(res) }).catch(function(err){ console.log(err) });
Promise.prototype.then()
鏈式調用,狀態變為resolve
如果把下一步想要的東西return出去,即可讓下一個then使用
var promise = new Promise(function(resolve,reject){ $.ajax({ url:'/api/poisearch.json', method:'get', datatype:'json', success:(res) =>{ resolve(res) }, error:(err)=>{ reject(err) } }) }) promise.then(function(res){ return res.data }).then(function(data){ return data.result; }).then(function(result){ console.log(result) });
上面的代碼還可以借用箭頭函數簡寫成,極大提升了代碼的簡潔性和可讀性
promise.then(res => res.data) .then(data => data.result) .then(result => console.log(result));
Promise.prototype.catch()
如果異步操作拋出錯誤,狀態就會變為Rejected,就會調用catch方法指定的回調函數,處理這個錯誤。
- 另外,then方法指定的回調函數,如果運行中拋出錯誤,也會被catch方法捕獲。
- 如果Promise狀態已經變成Resolved,再拋出錯誤是無效的。
- catch方法返回的還是一個Promise對象,因此后面還可以接着調用then方法。
Promise.all()
多個Promise實例,包裝成1個,有點類似Array.prototype.every()
用法:
var p = Promise.all([p1, p2, p3]);
- 只有p1、p2、p3的狀態都變成fulfilled,p的狀態才會變成fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。
- 只要p1、p2、p3之中有一個被rejected,p的狀態就變成rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。
Promise.race()
多個Promise實例,包裝成1個,有點類似Array.prototype.some()
- p1、p2、p3其中一個狀態變成fulfilled,p的狀態就會變成fulfilled
- 那個率先改變的Promise實例的返回值,就傳遞給p的回調函數。
done()、finally()
新的ES6中引入了promise的概念,目的是讓回調更為優雅。層層嵌套的回調會讓javascript失去美感和可讀性,同時javascript也推薦采用鏈式的方式去書寫函數調用。於是Promise就應運而生。Promise即承諾的意思,new一個Promise就是新建一個承諾。在新建一個承諾的時候你需要做兩件事情:
1.指定承諾所需完成的事
2.設置承諾是否實現的標准
下面我們來定義一個承諾:
1.承諾的內容:“獲取data.php的數據”,
2.承諾是否實現的評判是:“是否獲取data.php的數據 ”
這里我們會用到jQuery.ajax()方法,這會讓我們的代碼顯得簡單精煉。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
var
http = {
get:
function
(url) {
var
promise =
new
Promise(
function
(resolve, reject) {
$.ajax({
url: url,
method:
'get'
,
success:
function
(data) {
resolve(data);
},
error:
function
(xhr, statusText) {
reject(statusText);
}
});
});
return
promise;
}
};
http.get(
'data.php'
).then(
function
(data) {
document.write(data);
},
function
(err) {
document.write(err);
});
|
這里是獲取的data值
|
1
2
3
|
/* data.php文件 */
<?php
echo
'{"name":"Tom","age":"22"}'
;
|
在http請求中,我們定義了一個get方法,在get方法中,我們定義了一個promise的對象,而不是直接直接使用ajax獲取到我們想要的數據,在這個get方法,我們最后得到的是一個promise對象,對於這種需要等待的操作,我們采用promise去處理,返回給主線程的是一個promise對象,而不是一個最終的數據,這是延遲程序給主程序的一個承諾,主線程可以根據通過返回的promise對象獲取數據或者處理錯誤。這使得異步處理變得優雅而簡單。
在jQuery.ajax()中,我們采用了兩個方法來判斷是否實現了我們的承諾,它們分別是resove和reject方法,如果如果jQuery.ajax()執行了success,並且返回的data中有數據,那么我們就判定這個承諾已經實現了(fulfilled),則調用resolve方法。如果jQuery.ajax()執行了error,那么我們就判定這個承諾被拒絕了(rejected),則調用reject方法。
那resove和reject這兩個方法分別執行了哪些操作呢?這個我們就需要從Promise這個構造函數本身的結構說起。Promise這個構造函數結構大概是下面這樣的:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/* 用於描述思維的代碼 */
function
Promise(executor) {
// 共有三種狀態:pending(准備)、fulfilled(完成)、rejected(拒絕)
this
.PromiseStatus =
'pending'
;
// 用於存儲返回的數據
this
.PromiseValue;
// 完成
var
resolve =
function
(reson) {};
// 拒絕
var
reject =
function
(reson) {};
...
// 開始執行承諾
executor(resolve, reject);
}
Promise.prototype.then =
function
() {};
Promise.prototype.chain =
function
() {};
Promise.prototype.
catch
=
function
() {};
...
|
PromiseStatus:用於記錄Promise對象的三種狀態,這三中狀態分別是:
pending:待定狀態,Promise對象剛被初始化的狀態
fulfilled:完成狀態,承諾被完成了的狀態
rejected:拒絕狀態,承諾完成失敗的狀態
Promise的初始狀態是pending。隨后會更具承諾完成的情況更改PromiseStatus的值。
PromiseStatus:用於記錄返回的數據或者錯誤。當承諾完成時,會把返回的數據賦給PromiseStatus。如果承諾執行失敗了,那么失敗的原因也會賦給此變量。
resolve()和reject():Promise構造函數中有兩個閉包的函數resolve()和reject()。在new一個新的Promise的時候會傳遞一件你需要做的事情(executor)。這個executor是一個函數,在Promise的構造函數中它會被傳入兩個參數,這兩個參數即我們的兩個閉包的函數resolve()和reject(),以便你在executor中判定是否完成了你的承諾。
executor(): executor()函數中執行的代碼就是子程序需要完成事。在executor()函數內如果調用了resolve(),resolve()則會把Promise對象的狀態PromiseStatus修改為fulfilled,把resolve(value)中的value值賦給Promise對象的PromiseValue。然后再依次執行由then()方法構成的回調鏈中的回調函數。同樣,在executor()中調用reject()的過程也是類似的。調用過程如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/* 用於描述思維的代碼 */
executor(resolve, reject) {
...
resolve(value);
...
}
...
resolve(value) {
PromiseStatus =
'fulfilled'
;
PromiseValue = value;
...
// 接着調用回調鏈中的回調函數
}
|
then(onFulfilled, onRejected):這個方法實際上是把onFulfilled()函數和onRejected()函數添加到Promise對象的回調鏈中。回調鏈就像一個由函數組構成的隊列,每一組函數都是由至少一個函數構成(onFulfilled() 或者 onRejected() 或者 onFulfilled() 和 onRejected())。當resolve()或者reject()方法執行的時候,回調鏈中的回調函數會根據PromiseStatus的狀態情況而被依次調用。
onFulfilled(value)和onRejected(reason):參數value和reason的實參都是PromiseValue。這里有一點值得注意,如果onFulfilled(value)和onRejected(reason)這兩個回調函數中return返回值不是一個Promise的對象,那么這個返回值會被賦給PromiseValue,並在下一個then()的onFulfilled(value)和onRejected(reason)中做為實參使用。但如果這個返回值是一個Promise的對象,那么剩下的由then()構造的回調鏈會轉交給新的Promise對象並完成調用。
有這樣一個問題,如果在第一個then()中執行了onRejected(reason)回調函數,並且沒有return任何值的時候,那么下一個then()中將會調用onFulfilled(value)方法,而不是onRejected(reason)。因為在執行上一個then()的onRejected(reason)回調函數的時候並沒有出現錯誤或異常,所以PromiseStatus的狀態就被更改為fulfilled了。為了避免這個問題,我們可以在onRejected(reson)中返回一個Promise對象並reject()。代碼如下,我們要去訪問一個並不存在的文件solve.php:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
var
http = {
get:
function
(url) {
var
promise =
new
Promise(
function
(resolve, reject) {
$.ajax({
url: url,
method:
'get'
,
success:
function
(data) {
resolve(data);
},
error:
function
(xhr, statusText) {
reject(statusText);
}
});
});
return
promise;
}
};
http.get(
'solve.php'
).then(
function
(data) {
return
data;
},
function
(err) {
return
Promise.reject(
'Sorry, file not Found.'
);
}).then(
function
(data) {
document.write(data);
},
function
(err) {
document.write(err);
});
|
Promise.reject()方法會返回一個被reject()的Promise對象,因此使得我們可以繼續走下一個then()中的onRejected(reason)方法。

