promise是異步編程的一種解決方法,比傳統的回調函數和事件更合理更強大
。他由社區最早提出和實現,ES6將其寫進語言標准,統一了用法,原生提供了promise對象。
所謂promise,簡單說是一個容器,里面保存着某個未來才會結束的事件(通常是一個異步操作)的結果,從語法上說,promise是一個對象,從它可以獲取異步操作的消息,promise提供了統一的API,各種異步操作都可以用同樣的方法進行處理。
promise對象的特點
(1)對象的狀態不受外界影響,promise對象代表一個異步操作,有三種狀態,pending(進行中)、fulfilled(已成功)、rejected(已失敗)。只有異步操作的結果,可以決定當前是哪一種狀態,任何其他操作都無法改變這個狀態,這也是promise這個名字的由來“承若”;
(2)一旦狀態改變就不會再變,任何時候都可以得到這個結果,promise對象的狀態改變,只有兩種可能:從pending變為fulfilled,從pending變為rejected。這時就稱為resolved(已定型)。如果改變已經發生了,你再對promise對象添加回調函數,也會立即得到這個結果,這與事件(event)完全不同,事件的特點是:如果你錯過了它,再去監聽是得不到結果的。
有了Promise對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,是的控制異步操作更加容易。
Promise也有一些缺點。首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消。其次,如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。第三,當處於pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)
一 。用法:
promise 對象是一個構造函數,用來生成promise實例;
創建一個promise對象實例:
var promise =new Promise(function(resolve,reject){ //some code if(//異步操作成功) { resolve(value); } else{ reject(error); } });
Promise構造函數接收一個函數作為參數,該函數的兩個參數分別是resolve和reject,他們是兩個函數,由Javascript引擎提供,不用自己部署。
resolve函數的作用是,將promise對象的狀態從“pending”變為‘’resolved‘’,在異步操作成功時調用,並將異步操作的結果,作為參數傳遞出去;
reject函數的作用是,將Promise對象的狀態從“未完成”變為“失敗”(即從 pending 變為 rejected),在異步操作失敗時調用,並將異步操作報出的錯誤,作為參數傳遞出去。
promise實例生成以后,可以用then方法分別指定resolved狀態和rejected狀態的回調函數
promise.then( function(value){ //success } , function(error){ //failure } );
then方法可以接受連個回調函數作為參數,第一個回調函數是promise對象的狀態變為resolved時調用,第二個回調函數是promise對象的狀態變為rejected時調用,其中,第二個函數是可選的,不一定要提供,這兩個函數都接受promise對象傳出的值作為參數;
promise對象的簡單例子
<script>
function timeOut(ms){
return new Promise(function(resolve,reject){
return setTimeout(resolve,ms,'done');
})
}
timeOut(3000).then(function(value){
console.log(value);
})
</script>
timeOut方法返回一個promise實例,表示一段時間以后才會發生的結果,過了指定的時間(ms)以后,promise實例的狀態變為resolved,就會觸發then方法綁定的回調函數
Promise新建后會立即執行
let promise =new Promise(function(resolve,reject){ console.log('Promise'); resolve(); }); promise.then(function(){ console.log('resolved'); }); console.log('Hi!');
//Promise
// Hi!
// resolved
Promise 新建后立即執行,所以首先輸出的是Promise。然后,then方法指定的回調函數,將在當前腳本所有同步任務執行完才會執行,所以resolved最后輸出。
異步加載圖片的例子
<script>
function loadImageAsync (url) {
return new Promise( function( resolve, reject){
var image = new Image();
image .onoad =function(){
resolve(image); };
image. = function () {
reject(new Error("could not load image at "+ url) ); };
image.src =url;
}); }
loadImageAsync(url).then( function (value) {
console.log(value);
})
</script>
使用Promise包裝了一個圖片加載的異步操作。如果加載成功,就調用resolve方法,否則就調用reject方法。
promise對象實現ajax操作的例子
var getJSON = function (url) {
var promise = new Promise( function(resolve,reject ) {
var XHR = new XMLHttpRequest();
XHR.open("GET",url);
XHR.onreadystatechange = function () {
if (this.readyState !==4) {return;}
if(this.status == 200) {
resolve(this.response); } else{
reject(new Error(this.statusText) );
}
};
XHR.responseType = "json";
XHR.setRequestHeader("Accept","application/json");
XHR.send();
});
return promise;
};
getJSON("posts.json").then(function(json){
console.log("Contents : "+json );
}, function(error) {
console.log("出錯了", error);
}) ;
getJSON是對XMLHTTPRequest對象的封裝,用於發出一個針對JSON數據的HTTP請求,並且返回一個promise對象,需要注意的是,在getJSON內部,resolve函數和reject函數調用時,都帶有參數;
如果調用resolve函數和reject函數時帶有參數,那么他們的參數會被傳遞給回調函數,reject函數的參數通常是Error對象的實例,表示拋出的錯誤,resolve函數的參數除了正常的值以外,還可以是另一個promise實例;

p1和p2都是 Promise 的實例,但是p2的resolve方法將p1作為參數,即一個異步操作的結果是返回另一個異步操作。
這時p1的狀態就會傳遞給p2,也就是說,p1的狀態決定了p2的狀態。如果p1的狀態是pending,那么p2的回調函數就會等待p1的狀態改變;如果p1的狀態已經是resolved或者rejected,那么p2的回調函數將會立刻執行。
注意,調用resolve或reject並不會終結 Promise 的參數函數的執行。
調用resolve(1)以后,后面的console.log(2)還是會執行,並且會首先打印出來。這是因為立即 resolved 的 Promise 是在本輪事件循環的末尾執行,總是晚於本輪循環的同步任務。
一般來說,調用resolve或reject以后,Promise 的使命就完成了,后繼操作應該放到then方法里面,而不應該直接寫在resolve或reject的后面。所以,最好在它們前面加上return語句,這樣就不會有意外。

Promise.prototype.then()
promise實例具有then方法,也就是說,then方法定義在原型對象Promise.prototype上的。它的作用是為Promise實例添加狀態改變時的回調函數。前面說過,then方法的第一個參數是resolved狀態的回調函數,第二個參數是rejected狀態的回調函數。
then方法返回的是一個新的Promise實例(不是原來那個Promise實例),因此可以采用鏈式寫法,即then方法后面再調用另一個then方法

依次指定了兩個回調函數,第一個回到函數完成以后會將返回結果作為參數,傳入第二個回調函數。
采用鏈式的then,可以指定一組按照次序調用的回調函數。這時前一個回調函數,有可能返回的還是一個promise對象(即有異步操作),這時后一個回調函數,就會等待該promise對象的狀態發生變化,才會被調用。

上面第一個then方法指定的回調函數,返回的是另一個promise對象,這時,第二個then方法指定的回調函數,就會等待這個新的Promise對象狀態發生變化。如果變為resolved,就調用funA,如果狀態變為rejected,就調用funB。改為箭頭函數,代碼更簡潔:

