ES6中的Promise

哎呀,這個東西啊早就瞭解過了,但是奈何,受回調函數毒害日子太久了,以至於每次到了本可以使用promise來更加優雅的實現邏輯的時候,總是習慣性的使用了回調。這次又回頭看了一遍阮老師的es6中關於promise的講解,決定趁着熱乎,記錄下來,一來加深自己的理解,二來也提醒自己這是promise的時代了!
以下內容大多數來自阮老師的Es6入門
地址:http://es6.ruanyifeng.com/#docs/promise

什麼是promise?

Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大,或者說更加的優雅吧,傳統寫回調函數的方式,一個兩個到還好,可以一旦超過兩個,往往導致可讀性變差,話說當年我也是被回調函數正懵逼過多次,有時候自己寫的代碼自己回頭看都不知道是啥了。。。
所謂Promise,簡單說就是一個容器,裏面保存着某個未來纔會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統一的 API,各種異步操作都可以用同樣的方法進行處理。

兩個特點

  • 對象的狀態不受外界影響。
    既然promise代表的是一個異步的操作,操作就會對應着有狀態,promise對象中定義了三個狀態分別是:pending(進行中),resolved(已經完成),rejected(已經失敗),記住了只有異步操作的結果可以決定當前的promise是處於哪一種狀態,任何其他的操作都不能改變這個狀態。
  • 狀態一旦改變,就凍結了
    就是說promise對象狀態的改變只有兩種:從pending到resolved或者是從pending到rejected,只要這其中一種狀態發生,狀態就凝結了,會一直保持着這個結果。
    有了Promise對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操作更加容易。

缺點

說是缺點,但是我發現這確實我理解promise的一個突破點,那就是promise一旦新建它就會立即執行,無法中途取消,如果不設置回調函數,其內部拋出的錯誤也無法反應到外部(但是當你不需要捕捉錯誤的時候你就可以不設置它的回調啊O(∩_∩)O)

基本用法

var promise = new Promise(function(resolve,reject){
	//在這裏寫你的異步代碼
	if(異步操作成功){
		resolve(resp)
	}else{
		reject(error)
	}
})

注意理解這個定義式的例子很重要,Promise是一個構造函數它自身接受一個函數作爲參數,而這個參數函數又有兩個定義好的參數那就是resolve和reject,千萬不要陷入對於這兩個函數是從何而來,如何實現之類的迷糊旋渦中,這兩個函數是JavaScript引擎提供的不需要自己部署!可以理解爲是兩個信使或者說是預先提供給你使用的兩個trigger(觸發器),你可以用resolve觸發器來將異步操作成功後的結果傳遞出去,對應的你可以用reject觸發器來將異步操作失敗後的錯誤對象傳遞出去。同時很重要的一點就是,兩個觸發器的觸發對應着promise狀態的改變,resolve使得promise的狀態由pending變爲resolvereject使得promise的狀態由pending變爲reject。
好了這樣我們就把異步操作的主邏輯寫好了,簡單概括起來就是用一個promise實例當容器(對它就是個容器,這個比喻太形象了),把異步代碼放進去,並自己寫好異步代碼結束後根據操作結果指定resolve方法和reject方法。然後我們就可以跳出這個構造函數,到外面來用then來接受這個容器傳遞出來給我們的東西了,因爲這個既可能是成功的結果也可能是失敗的結果所以理所當然的我們的then方法也接受兩個回調函數來做處理,第一個函數是Promise對象狀態變爲Resolve時候調用的,第二個函數是Promise對象狀態變爲Reject時候調用的,其中第二個函數是可選的,意味着你可以在then中只寫一個回調函數哦,那他就是resolve的時候回調用的(那reject的時候怎麼辦?不怎麼辦,啥都不辦☺),對就是這麼簡單!
下面是一個Promise對象的簡單例子。

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'done');
  });
}

timeout(100).then((value) => {
  console.log(value);
});

上面代碼中,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最後輸出

下面是一個用Promise對象實現的 Ajax 操作的例子。

var getJSON = function(url) {
var promise = new Promise(function(resolve, reject){
var client = new XMLHttpRequest();
client.open(“GET”, url);
client.onreadystatechange = handler;
client.responseType = “json”;
client.setRequestHeader(“Accept”, “application/json”);
client.send();

function handler() {
  if (this.readyState !== 4) {
    return;
  }
  if (this.status === 200) {
    resolve(this.response);
  } else {
    reject(new Error(this.statusText));
  }
};

});

return promise;
};

getJSON("/posts.json").then(function(json) {
console.log('Contents: ’ + json);
}, function(error) {
console.error(‘出錯了’, error);
});
上面代碼中,getJSON是對 XMLHttpRequest 對象的封裝,用於發出一個針對 JSON 數據的 HTTP 請求,並且返回一個Promise對象。需要注意的是,在getJSON內部,resolve函數和reject函數調用時,都帶有參數。

如果調用resolve函數和reject函數時帶有參數,那麼它們的參數會被傳遞給回調函數。reject函數的參數通常是Error對象的實例,表示拋出的錯誤;

resolve函數的參數除了正常的值以外,還可能是另一個 Promise 實例,表示異步操作的結果有可能是一個值,也有可能是另一個異步操作,比如像下面這樣。

var p1 = new Promise(function (resolve, reject) {
// …
});

var p2 = new Promise(function (resolve, reject) {
// …
resolve(p1);
})

上面代碼中,p1和p2都是Promise的實例,但是p2的resolve方法將p1作爲參數,即一個異步操作的結果是返回另一個異步操作。

注意,這時p1的狀態就會傳遞給p2,也就是說,p1的狀態決定了p2的狀態。如果p1的狀態是Pending,那麼p2的回調函數就會等待p1的狀態改變;如果p1的狀態已經是Resolved或者Rejected,那麼p2的回調函數將會立刻執行。

臨近結尾

做個小總結吧,是不是覺得自己反反覆覆看了好多次的Promise,每次似乎都感覺理解了但是就是不知道怎麼用?我來告訴你怎麼用
其實沒那麼複雜,比如你要做一個異步的操作A好了,抄起鍵盤就敲

var promise = newPromise (function(resolve,reject{
	A   //   直接在這裏寫你A的代碼就好了
	if(A操作成功){
		resolve(resp)
	}else{
		reject(error)
	}
}))

ok就醬紫主邏輯就寫好了,然後你就可以愉快的在外面使用then來鏈式的寫你的回調函數中的處理邏輯了。

promise.then(function(resp){
	this.format(resp)
},function(error){
	console.log(error)
})

就這樣就好了呀,反覆強調注意的一點就是,resolve 和reject是兩個定義並實現好的方法,它們有JavaScript引擎提供,你只需要知道異步操作成功就指定resolve方法,操作失敗就指定reject方法即可(是不是很傻瓜),不用加this.resolve()或者是this.reject(),直接寫resolve(resp)和reject(error)瀏覽器會執行的,放心去外面用then接受傳出來的東西吧。另一點就是可能有些人會記錯成一個Promise對應兩個then方法,會認爲需要兩個then方法來分別接一個Promise實例返回出來的成功的回調很失敗的回調這個是認識上的大錯誤,一個Promise只需要一個then方法來接就行了,then方法中直接寫好兩個回調,排在前面的是成功的回調,排在第二的是失敗的回調,then方法後面還可以繼續跟好多個then方法那是因爲前面的then方法返回的又是一個新的Promise實例,而不是爲resolve和reject分別制定兩個then。
好了,寫完了,關於Promise還有fetchAPI我在GitHub上寫了連個炒雞剪短的實例代碼,有興趣可以去瞅瞅哦,如果有一點點幫到你,是我的榮幸,希望能給我顆小星星哦;
fetch:https://github.com/CoffeeandTea/fetchdemo
fetch+Promise:https://github.com/CoffeeandTea/fetch-promise
flex佈局demo:https://github.com/CoffeeandTea/flex-layout
webWorker: https://github.com/CoffeeandTea/Webworker

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章