剛開始寫前端的時候,處理異步請求常常用callback,簡單又順手。後來寫着寫着就拋棄了callback,開始用promise來處理異步問題。promise寫起來確實更加優美,但因爲缺少對它內部結構的深入認識,每次在遇到一些複雜的狀況時,promise用起來老是不那麼駕輕就熟,debug也得搞半天。前端
因此,這篇文章我會帶你們從零開始,手寫一個基本能用的promise。跟着我寫下來之後,你會對promise是什麼以及它的內部結構有一個清楚的認知,將來在複雜場景下使用promise也能如魚得水。segmentfault
回到正文,什麼是Promise?說白了,promise就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。promise
首先,ES6規定Promise對象是一個構造函數,用來生成Promise實例。而後,這個構造函數接受一個函數(executor)做爲參數,該函數的兩個參數分別是resolve和reject。最後,Promise實例生成之後,能夠用then方法分別指定resolved狀態和rejected狀態的回調函數(onFulfilled和onRejected)。緩存
具體的使用方法,用代碼表現是這樣:併發
const promise = new Promise(function(resolve, reject) { // ... some code if (/* 異步操做成功 */){ resolve(value); } else { reject(error); } }); promise.then(function(value) { // success }, function(error) { // failure });
理解了這個後,咱們就能夠大膽的開始構造咱們本身的promise了,咱們給它取個名字:CutePromise異步
咱們直接用ES6的class來建立咱們的CutePromise,對ES6語法還不熟悉的,能夠先讀一下個人另外兩篇介紹ES6核心語法的文章後再回來。30分鐘掌握ES6/ES2015核心內容(上)、30分鐘掌握ES6/ES2015核心內容(下)函數
class CutePromise { // executor是咱們實例化CutePromise時傳入的參數函數,它接受兩個參數,分別是resolve和reject。 // resolve和reject咱們將會定義在constructor當中,供executor在執行的時候調用 constructor(executor) { const resolve = () => {} const reject = () => {} executor(resolve, reject) } // 爲實例提供一個then的方法,接收兩個參數函數, // 第一個參數函數必傳,它會在promise已成功(fulfilled)之後被調用 // 第二個參數非必傳,它會在promise已失敗(rejected)之後被調用 then(onFulfilled, onRejected) {} }
建立了咱們的CutePromise後,咱們再來搞清楚一個關鍵點:Promise 對象的狀態。oop
Promise 對象經過自身的狀態,來控制異步操做。一個Promise 實例具備三種狀態:測試
上面三種狀態裏面,fulfilled和rejected合在一塊兒稱爲resolved(已定型)。狀態的切換隻有兩條路徑:第一種是從pending=>fulfilled,另外一種是從pending=>rejected,狀態一旦切換就不能再改變。this
如今咱們來爲CutePromise添加狀態,大概流程就是:
首先,實例化初始過程當中,咱們先將狀態設爲PENDING
,而後當executor執行resolve的時候,將狀態更改成FULFILLED
,當executor執行reject的時候將狀態更改成REJECTED
。同時更新實例的value。
constructor(executor) { ... this.state = 'PENDING'; ... const resolve = (result) => { this.state = 'FULFILLED'; this.value = result; } const reject = (error) => { this.state = 'REJECTED'; this.value = error; } ... }
再來看下咱們的then函數。then函數的兩個參數,onFulfilled表示當promise異步操做成功時調用的函數,onRejected表示當promise異步操做失敗時調用的函數。假如咱們調用then的時候,promise已經執行完成了(當任務是個同步任務時),咱們能夠直接根據實例的狀態來執行相應的函數。假如promise的狀態仍是PENDING, 那咱們就將onFulfilled和onRejected直接存儲到chained這個變量當中,等promise執行完再調用。
constructor(executor) { ... this.state = 'PENDING'; // chained用來儲存promise執行完成之後,須要被依次調用的一系列函數 this.chained = []; const resolve = (result) => { this.state = 'FULFILLED'; this.value = result; // promise已經執行成功了,能夠依次調用.then()函數裏的onFulfilled函數了 for (const { onFulfilled } of this.chained) { onFulfilled(res); } } ... } then(onFulfilled, onRejected) { if (this.state === 'FULFILLED') { onFulfilled(this.value); } else if (this.state === 'REJECTED') { onRejected(this.value); } else { this.$chained.push({ onFulfilled, onRejected }); } }
這樣咱們就完成了一個CutePromise的建立,下面是完整代碼,你們能夠複製代碼到控制檯測試一下:
class CutePromise { constructor(executor) { if (typeof executor !== 'function') { throw new Error('Executor must be a function'); } this.state = 'PENDING'; this.chained = []; const resolve = res => { if (this.state !== 'PENDING') { return; } this.state = 'FULFILLED'; this.internalValue = res; for (const { onFulfilled } of this.chained) { onFulfilled(res); } }; const reject = err => { if (this.state !== 'PENDING') { return; } this.state = 'REJECTED'; this.internalValue = err; for (const { onRejected } of this.chained) { onRejected(err); } }; try { executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled, onRejected) { if (this.state === 'FULFILLED') { onFulfilled(this.internalValue); } else if (this.$state === 'REJECTED') { onRejected(this.internalValue); } else { this.chained.push({ onFulfilled, onRejected }); } } }
提供一下測試代碼:
let p = new CutePromise(resolve => { setTimeout(() => resolve('Hello'), 100); }); p.then(res => console.log(res)); p = new CutePromise((resolve, reject) => { setTimeout(() => reject(new Error('woops')), 100); }); p.then(() => {}, err => console.log('Async error:', err.stack)); p = new CutePromise(() => { throw new Error('woops'); }); p.then(() => {}, err => console.log('Sync error:', err.stack));
實現鏈式調用其實很簡單,只須要在咱們定義的then()方法裏返回一個新的CutePromise便可。
then(onFulfilled, onRejected) { return new CutePromise((resolve, reject) => { const _onFulfilled = res => { try { //注意這裏resolve有可能要處理的是一個promise resolve(onFulfilled(res)); } catch (err) { reject(err); } }; const _onRejected = err => { try { reject(onRejected(err)); } catch (_err) { reject(_err); } }; if (this.state === 'FULFILLED') { _onFulfilled(this.internalValue); } else if (this.state === 'REJECTED') { _onRejected(this.internalValue); } else { this.chained.push({ onFulfilled: _onFulfilled, onRejected: _onRejected }); } }); }
不過,咱們還須要解決一個問題:假如then函數的第一個參數onfulfilled()自己返回的也是一個promise怎麼辦?好比下面這種使用方式,實際上是最真實項目場景中最多見:
p = new CutePromise(resolve => { setTimeout(() => resolve('World'), 100); }); p. then(res => new CutePromise(resolve => resolve(`Hello, ${res}`))). then(res => console.log(res));
因此咱們須要讓咱們的resolve方法可以處理promise:
const resolve = res => { if (this.state !== 'PENDING') { return; } // 假如說res這個對象有then的方法,咱們就認爲res是一個promise if (res != null && typeof res.then === 'function') { return res.then(resolve, reject); } ... }
以上三道思考題其實跟你用不用promise並無多大關係,可是若是你不深入理解promise想要解決這三個問題還真不是那麼輕鬆的。