Promise (1) 初步接觸
總想著王者榮耀排位賽再提升個等級就棄掉遊戲好好學習,然而打了兩個周也沒升上去,看來是應該換個方向發揮了。
最近看了《javascript Promise迷離書》,對Promise的理解頗有加深。那麽就從總結Promise開始吧。
1 什麽是Promise?
抽象描述: promise是一個規範,提供了一套精心定義的、用來與代表一個可能會在任意時刻完成或失敗的異步過程的結果的對象交互的接口。“Promise把異步處理對象和處理規則進行規範化, 並按照采用統一的接口來編寫,而采取規定方法之外的寫法都會出錯。”--《javascript Promise迷離書》第一章。(大概就是知道用promise來幹什麽的時候才能清楚這段話的意思吧)
簡單描述:promise是一個異步對象,在Promise對象創建時可能是未知的,狀態變換後Promise表示一個異步操作的最終結果,與之進行交互的方式主要是 then
方法,該方法註冊了兩個回調函數,它允許你為異步代碼執行結果的成功和失敗分別綁定相應的處理方法(handlers ),用於處理resolve的value(常用變量名)或reject 的reason(常用變量名)。
1.1 Promise的構造函數(Constructor)
new Promise構造器之後,會返回一個promise對象,創建Promise對象的基本語法:
new Promise(function(resolve, reject) {// 異步處理 // 處理結束後、判定什麽情況下resolve這個Promise,什麽情況下reject這個Promise. // 通常resolve(value),將promise的狀態轉變成成功,value為之後要用的值 // 通常reject(reason),將promise的狀態轉變為失敗,reason為之後錯誤處理需要用的值 if ( /* 異步操作成功的判定條件 */ ) { resolve(value); } else { reject(reason); } });
new一個Promise對象,給Promise構造函數傳遞的參數是一個函數
function Promise(resolver) { resolver(function resolvePromise(value) { _resolve( /*......*/ , value); }, function rejectPromise(reason) { _reject( /*......*/ , reason); }); }
那麽resolve和reject主要做了什麽事情了?如下圖創建的testResolvePromise,在Promise構造函數接受的函數內部執行了 resolve。testResolvePromise對象的[[PromiseStatus]]值是"resolved",[[PromiseValue]]值"resolve: value"(這個字符串就是我們傳入resolve函數的值)。對比testRrejectPromise,testRrejectPromise對象的[[PromiseStatus]]值是"rejected",[[PromiseValue]]值"reject:reason"(這個字符串就是我們傳入reject函數的值)。resolve和reject改變了promise的狀態。同時將處理後的值賦給了Promise對象的某個屬性(為什麽是處理後,見後文。如果你resolve的value值是一個Promise對象,那麽就不是簡單的賦值了。)。
總結:給Promise構造函數傳遞的參數是一個函數(是一個函數)。
1.2 Promise的狀態
用new Promise 實例化的promise對象有三個狀態: pending,fulfilled,rejected。
pending: promise對象剛被創建的初始狀態,既未完成也沒有失敗的狀態,此狀態可以遷移至fulfilled和rejected狀態。
fulfilled:意味著操作成功完成,resolve(成功)時,此時的狀態不能遷移(不能改變的)。
rejected:意味著操作失敗reject(失敗)時,此時的狀態不能遷移(不能改變的)。
eg: 如圖創建了一個testPromisePending對象,用setTimeout設置一個時間10秒後再執行resolve。在這時間段前還沒有執行resolve,此時的testPromisePending的[[PromiseStatus]]值是"pending"。10秒過後再在控制太輸出一次testPromisePending,此時它的狀態就遷移了[[PromiseStatus]]值是"resolved",即是fulfilled狀態。
“promise對象的狀態,從Pending轉換為Fulfilled或Rejected之後, 這個promise對象的狀態就不會再發生任何變化”,--《javascript Promise迷離書》(書中的流程圖十分的清晰,有利於promise的工作流程理解)
pending狀態---->resolve(value)----->fulfilled狀態.
pending狀態---->reject(reason)----->rejected狀態.
總結:如果一個promise不是pending狀態,就說明這個promise是settled(不變的),它要麽fulfilled狀態要麽是rejected狀態。
1.3 Promise的then方法
把這個方法單獨拿出來說,只是為了承接上文,Promise構造函數接受一個函數作為參數,函數裏面內部代碼執行了resolve或reject後續可以幹什麽,有resolve(value)或 reject(reason)傳遞了值後續怎麽處理?這時候then方法閃亮登場了,promise的then方法裏面可以設置resolve(value) 或reject(reason)時調用的回調函數。then方法接受兩個可選參數,當兩個參數不是函數就會被忽略掉。
var testPromise = new Promise(function(resolve, reject) { if ( /* 異步操作成功的判定條件 */ ) { resolve(value); //函數內部resolve了value值,那麽我們怎麽處理value值了; } else { reject(error); //函數內部reject了reason值,那麽我們怎麽處理reason值了; } }); testPromise.then(function onFulfilled(value) { //當testPromise的狀態是fulfilled的時候執行 }, function onRejected(reson) { //當testPromise的狀態是rejected的時候執行 })
pending狀態---->resolve(value)----->fulfilled狀態---->執行then方法裏面onFulfilled(value)方法
pending狀態---->reject(reason)----->rejected狀態---->執行then方法裏面onRejected(reason)方法
當testPromise的狀態遷移成 fulfilled或rejected的時候時才執行後續的then方法。testPromise的狀態是fulfilled就執行onFulfilled方法,此時的value參數的值就是之前resolve的值,onFulfilled函數內部就可以對fulfilled的promise和傳遞進來value值進行後續的處理了。testPromise的狀態是rejected就執行onRejected方法,此時的reason參數的值就是之前reason的值,onRejected函數內部就可以對rejected的promise和傳遞進來reason值進行後續的處理了。 (上面的路線只會執行一條,)
對了then執行完後返回的結果還是一個promise對象,媽呀這麽執行下去promise是不是沒完沒了呀?對呀對呀這就是後續為什麽promise適合處理某些場景的原因之一(加粗,加粗,後面需要解釋)。每次調用then都會返回一個新創建的promise,神奇了!!還有更更神奇的事兒,這個新創建的promise跟then方法執行的回調onFulfilled和onRejected有關系(廢話)。
⑴.調用then方法會返回的promise是新創建的
⑵.這個新創建promise的值跟onFulfilled和onRejected的函數內部有無return 以及return的值有關系。如果沒有return則返回一個狀態為fulfilled的[[PromiseValue]](不同實現內部屬性不一定叫PromiseValue)為undefined的promise對象。
⑶.承接⑵如果有return,retuen 一個普通的object對象那麽新創建promise對象的 [[PromiseValue]] 的值就等於 object。 如果 return 的是一個Promise對象,還是會返回一個新的promise對象,且屬性值和return 的Promise對象值一樣
返回了一個普通的promise,imANewPromiseB長什麽樣子
var testPromiseA = new Promise(function(resolve, reject) { resolve("testPromiseA") }); var testForreturnPromise = new Promise(function(resolve, reject) { resolve("just test for testPromiseB") }); //返回了一個普通的promise,imANewPromiseB長什麽樣子 var imANewPromiseB = testPromiseA.then(function onFulfilled(value) { //返回了一個promise return testForreturnPromise }) // 在控制臺輸出,imANewPromiseB和 testForreturnPromise 的屬性值一樣,但他們不是同一個promise。 //imANewPromiseB 是一個新的promise 對象
在控制臺輸出,imANewPromiseB和 testForreturnPromise 的屬性值一樣,但他們不是同一個promise,且imANewPromiseB和testPromiseA也不是同一個promise 對象。imANewPromiseB是一個新的promise。
總結:每次調用then都會返回一個新創建的promise對象。
1.4 為啥要用promise?
有一個作用就是把代碼從異步回調函數拯救出來,讓代碼看起來邏輯清晰可愛。因為Promise把異步處理對象和處理規則進行規範化。
function getJSON(url) { return new Promise(function(resolve, reject) { let xhr = new XMLHttpRequest(); //神奇的對象 xhr.open(‘GET‘, url); xhr.onreadystatechange = handler; // 無論readyState值何時發生改變,XMLHttpRequest對象都會激發一個readystatechange事件,handler被調用,然後根據結果resolve,或者reject xhr.responseType = ‘json‘; xhr.setRequestHeader(‘Accept‘, ‘application/json‘); xhr.send(); function handler() { if (this.readyState === this.DONE) { if (this.status === 200) { resolve(this.response); //successDo(this.response) } else { reject(new Error(‘getJSON: `‘ + url + ‘` failed with status: [‘ + this.status + ‘]‘)); //faileDo(this.response) } } }; }); } // 使用promise getJSON(url).then(function onFulfilled(value) { //successDo }, function onRejected(reson) { //faileDo }) // 使用回調 getJSON(url, successDo, faileDo)
使用回調的方式來做getJSON,拿數據和對拿到數據成功和失敗都在一個函數裏面操作處理。
使用promise來做getJSON,getJSON只需要做好自己拿數據,且通過resolve和reject返回拿數據成功還是失敗的邏輯,並不關心成功或失敗的處理。後續的then方法會根據getJSON返回的promise的狀態執行onFulfilled方法或者onRejected方法來處理,數據拿成功或者數據拿失敗的邏輯。這個流程比較符合人類(我這種人類)的習慣,一步一步執行操作,拿數據返回成功或失敗----->處理拿數據成功或失敗。
總結:Promise 可以讓異步處理對象更像一個流程操作。使用Promise 的時候要思考Promise 的適用場景。並不是說在異步處理的時候Promise永遠都是最好的選擇。
Promise (1) 初步接觸