Promise有哪幾種狀態,各個狀態之間是如何進行轉換的?
一、什麼是Promise?
1.Promise的結構:
class Promise{ constructor(exector){ function resolve(){ } function reject(){ } exector(resolve,reject) } then(){ } }
2.Promise的三種狀態:
pending、fulfilled、rejected(未決定,履行,拒絕),同一時間只能存在一種狀態,且狀態一旦改變就不能再變。promise是一個建構函式,promise物件代表一項有兩種可能結果(成功或失敗)的任務,它還持有多個回撥,出現不同結果時分別發出相應回撥。
1.初始化,狀態:pending 2.當呼叫resolve(成功),狀態:pengding=>fulfilled 3.當呼叫reject(失敗),狀態:pending=>rejected
const PENDING = "pending";//Promise會一直保持掛起狀態,知道被執行或拒絕。 const FULFULLED = "fulfilled"; const REJECTED = "rejected"; class Promise{ constructor(exector){ let self = this;//快取當前promise物件 self.status = PENDING;//初始狀態,對promise物件呼叫state(狀態)方法,可以檢視其狀態是“pending"、"resolved"、還是”rejected“ self.value = undefined;// fulfilled狀態時 返回的資訊 self.reason = undefined;// rejected狀態時 拒絕的原因 self.onResolveCallBacks = [];// 儲存resolve(成功)狀態對應的onFulfilled函式 self.onRejectCallBacks = [];// 儲存rejected(失敗)狀態對應的onRejected函式 let resolve = (value) => {//成功 if(self.status === PENDING){//如果成功則,狀態由pending=>fulfilled self.status = FULFULLED; self.value = value; self.onResolveCallBacks.forEach(cb=>cb(self.value));//執行釋出 } } let reject = (reason) => {//失敗 if(self.status === PENDING){//如果失敗,則狀態由pending=>rejected self.status = REJECTED; self.reason = reason; self.onRejectCallBacks.forEach(cb=>cb(self.reason));//執行釋出 } } try{ exector(resolve,reject) }catch(e){ reject(e) } } then(onFulfilled,onRejected){ let self=this; if(self.status === FULFULLED){ onFulfilled(self.value);//成功值 } if(self.status === REJECTED){ onFulfilled(self.reason);//拒絕原因 } if(self.status === PENDING){ self.onResolveCallBacks.push(onFulfilled);//訂閱釋出 self.onRejectCallBacks.push(onRejected);//訂閱釋出 } } //promise的決議結果只有兩種可能:完成和拒絕,附帶一個可選的單個值。如果Promise完成,那麼最終的值稱為完成值;如果拒絕,那麼最終的值稱為原因。Promise只能被決議(完成或拒絕)一次。之後再次試圖完成或拒絕的動作都會被忽略。 } new Promise((resolve,reject)=>{ resolve("挖坑妹"); //非同步處理 //處理結束後、呼叫resolve或reject }).then((data)=>{ console.log(data);//"挖坑妹" },(reason)=>{ console.log(reason); })
3.promise的優缺點
優點:
1.Promise 分離了非同步資料獲取和業務邏輯,有利於程式碼複用。
2.可以採用鏈式寫法
3.一旦 Promise 的值確定為fulfilled 或者 rejected 後,不可改變。
缺點:
程式碼冗餘,語義不清。
二、為什麼用Promise?
1.解決回撥地獄
回撥地獄:傳送多個非同步請求時,每個請求之間相互都有關聯,會出現第一個請求成功後再做下一個請求的情況。我們這時候往往會用巢狀的方式來解決這種情況,但是這會形成”回撥地獄“。如果處理的非同步請求越多,那麼回撥巢狀的就越深。出現的問題:
1.程式碼邏輯順序與執行順序不一致,不利於閱讀與維護。
2.非同步操作順序變更時,需要大規模的程式碼重構。
3.回撥函式基本都是匿名函式,bug追蹤困難。
const request = url => { return new Promise((resolve,reject) => { $.get(url,params => { resolve(params) }); }); }; request(url).then(params1 => { return request(params1.url); }).then(params2 => { return request(params2.url); }).then(params3 => { console.log(params3); }).catch(err => throw new Error(err));
2.解決非同步
我們都知道js是單執行緒執行程式碼,導致js的很多操作都是非同步執行(ajax)的,以下是解決非同步的幾種方式:
1.回撥函式(定時器)。
2.事件監聽。
3.釋出/訂閱。
4.Promise物件。(將執行程式碼和處理結果分開)
5.Generator。
6.ES7的async/await。
三、怎麼用Promise?
promise有幾種物件方法
1.then方法(非同步執行)
當resolve(成功)/reject(失敗)的回撥函式
//onFulfilled 是用來接收promise成功的值 //onRejected 是用來接收promise失敗的原因 promise.then(onFulfilled,onRejected)
2.resolve(成功)
呼叫onFulfilled
const promise = new Promise((resolve,reject)=>{ resolve('fulfilled');//狀態:pending=>fulfilled }); promise.then(result =>{//onFulfilled呼叫 console.log(result);//'fulfilled' },result =>{//onRejected不呼叫 }) //注:resolve使用 Promise.resolve('hellow world')相當於 //相當於 const promise = new Promise(resolve=>{ resolve('hellow world'); })
3.reject(失敗)
呼叫onRejected
const promise = new Promise((resolve,reject)=>{ reject('rejected');//狀態:pending=>rejected }); promise.then(result =>{//onFulfilled不呼叫 },result =>{//onRejected呼叫 console.log(result);//'rejected' }) //注:reject使用 Promise.reject('err')相當於 //相當於 const promise = new Promise((resolve,reject)=>{ reject('err'); });
4.catch
鏈式寫法中可以捕獲前面then中傳送的異常,這種寫法的好處在於先執行promise操作,然後根據返回的結果(成功或失敗)來呼叫onFulfilled(或者onRrejected)函式。
promise.then(onFulfilled).catch(onRrejected);
5.all
Promise.all接收一個promise物件陣列為引數,處理並行非同步操作會用到,但是需要全部為resolve才能呼叫。這種情況是幾個任務可以並行執行
const promise1= new Promise((resolve,reject)=>{ resolve('promise1'); }); const promise2= new Promise((resolve,reject)=>{ resolve('promise2'); }); const promise3= new Promise((resolve,reject)=>{ resolve('promise3'); }); Promise.all([promise1, promise2, promise3]).then(data => { console.log(data); // ['promise1', 'promise2', 'promise3'] 結果順序和promise例項陣列順序是一致的 }, err => { console.log(err); });
可以從一個promise物件派生出新的promise物件,我們可以要求代表著並行任務的兩個promise物件合併成一個promise物件,由後者負責通知前面的那些任務都已完成。也可以要求代表著任務系列中首要任務的Promise物件派生出一個能代表任務系列中末任務的Promise物件,這樣後者就能知道這一系列的任務是否均已完成。
6.race
Promise.race接收一個promise物件陣列為引數,只要有一個promise物件進入Fulfilled或者Rejected狀態的話,就會進行後面的處理。這可以解決多個非同步任務的容錯
function racePromise(time){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve(time); },time) }) } var startDate = Date.now(); Promise.race([ racePromise(5), racePromise(50), racePromise(500), ]).then(function(values){ console.log(values);5 })