寫這篇文章的目的是解剖Promise源碼,原由也是最近秋招被問到了讓手寫Promise,另外在網上看到的Promise源碼或多或少有些小問題,也就是沒有徹底遵循Promise/A+規範。javascript
代碼會徹底使用ES6
語法,主要分如下幾個模塊:java
所謂Promise就是一個容器,有三個狀態:PENDING(進行中)、FULFILLED(成功)、REJECTED(失敗),裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果,有兩大特色:數組
來看下Promise的用法:promise
new Promise((resolve, reject) => {
// ...
// 成功則執行resolve,不然指定reject
}).then(
res => {
// resolve對應觸發函數的執行
},
err => {
// reject對應觸發函數的執行
}
).then(
// 支持鏈式調用
res => {
}
).catch(
err => console.log(err)
)
Promise.resolve();
Promise.reject();
Promise.all([promise1, promise2, ...]).then();
Promise.race([promise1, promise2, ...]).then();
複製代碼
經過用法不難分析出:markdown
then、catch
方法resolve、reject、all、race
那麼能夠寫出大體結構代碼:dom
class Promise {
constructor(exector) {
const resolve = () => {
}
const reject = () => {
}
exector(resolve, reject);
}
then() {
}
catch() {
}
static resolve() {
}
static reject() {
}
static all() {
}
static race() {
}
}
複製代碼
以後在此基礎上補充代碼。異步
首先引入三種狀態,完善resolve
、reject
函數,最後在構造函數內執行exector(resolve, reject)
:函數
// 定義三種狀態
const PENDING = 'PENDING'; // 進行中
const FULFILLED = 'FULFILLED'; // 已成功
const REJECTED = 'REJECTED'; // 已失敗
class Promise {
constructor(exector) {
// 初始化狀態
this.status = PENDING;
// 將成功、失敗結果放在this上,便於then、catch訪問
this.value = undefined;
this.reason = undefined;
const resolve = value => {
// 只有進行中狀態才能更改狀態
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
}
}
const reject = reason => {
// 只有進行中狀態才能更改狀態
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
}
}
// 當即執行exector
// 把內部的resolve和reject傳入executor,用戶可調用resolve和reject
exector(resolve, reject);
}
}
複製代碼
注意:exector(resolve, reject);
執行可能會報錯,因此須要使用try
包括一下,有報錯reject
拋出去。測試
constructor(exector) {
// 初始化狀態
this.status = PENDING;
// 將成功、失敗結果放在this上,便於then、catch訪問
this.value = undefined;
this.reason = undefined;
const resolve = value => {
if (this.status === PENDING) {
// 只有進行中狀態才能更改狀態
this.status = FULFILLED;
this.value = value;
}
}
const reject = reason => {
if (this.status === PENDING) {
// 只有進行中狀態才能更改狀態
this.status = REJECTED;
this.reason = reason;
}
}
// 修改代碼
try {
// 當即執行executor
// 把內部的resolve和reject傳入executor,用戶可調用resolve和reject
exector(resolve, reject);
} catch(e) {
// executor執行出錯,將錯誤內容reject拋出去
reject(e);
}
}
複製代碼
此時可使用then
進行捕獲了,then
接收兩個函數,分別對應FULFILLED
和REJECTED
狀態:ui
new Promise().then(
res => {},
err => {},
)
複製代碼
注意:then
、catch
是微任務,這裏使用setTimeout模擬:
then(onFulfilled, onRejected) {
// then是微任務,這裏用setTimeout模擬
setTimeout(() => {
if (this.status === FULFILLED) {
// FULFILLED狀態下才執行
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// REJECTED狀態下才執行
onRejected(this.reason);
}
})
}
複製代碼
OK,第一版已經完成:
// 定義三種狀態
const PENDING = 'PENDING'; // 進行中
const FULFILLED = 'FULFILLED'; // 已成功
const REJECTED = 'REJECTED'; // 已失敗
class Promise {
constructor(exector) {
// 初始化狀態
this.status = PENDING;
// 將成功、失敗結果放在this上,便於then、catch訪問
this.value = undefined;
this.reason = undefined;
const resolve = value => {
if (this.status === PENDING) {
// 只有進行中狀態才能更改狀態
this.status = FULFILLED;
this.value = value;
}
}
const reject = reason => {
if (this.status === PENDING) {
// 只有進行中狀態才能更改狀態
this.status = REJECTED;
this.reason = reason;
}
}
try {
// 當即執行executor
// 把內部的resolve和reject傳入executor,用戶可調用resolve和reject
exector(resolve, reject);
} catch(e) {
// executor執行出錯,將錯誤內容reject拋出去
reject(e);
}
}
then(onFulfilled, onRejected) {
// then是微任務,這裏用setTimeout模擬
setTimeout(() => {
if (this.status === FULFILLED) {
// FULFILLED狀態下才執行
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// REJECTED狀態下才執行
onRejected(this.reason);
}
})
}
}
複製代碼
能夠拿數據測試一下:
const promise = new Promise((resolve, reject) => {
Math.random() < 0.5 ? resolve(1) : reject(-1);
}).then(
res => console.log(res),
err => console.log(err),
)
複製代碼
此時第一版還有三個方向須要完善:
開發中常常會將接口放於promise
內部,等接口請求響應成功把數據resolve
出去,或失敗時把數據reject
出去,此時then
、catch
纔會進行捕獲。
而如今的代碼,promise
內部若是有異步代碼執行後才resolve
,then
不會等待異步代碼執行完畢會直接執行,因此此時狀態是PENDING
,不會觸發then
的回調函數。
新增onFulfilledCallbacks、onRejectedCallbacks維護成功態、失敗態任務隊列:
// 定義三種狀態
const PENDING = 'PENDING'; // 進行中
const FULFILLED = 'FULFILLED'; // 已成功
const REJECTED = 'REJECTED'; // 已失敗
class Promise {
constructor(exector) {
// 初始化狀態
this.status = PENDING;
// 將成功、失敗結果放在this上,便於then、catch訪問
this.value = undefined;
this.reason = undefined;
// 新增代碼:
// 成功態回調函數隊列
this.onFulfilledCallbacks = [];
// 失敗態回調函數隊列
this.onRejectedCallbacks = [];
const resolve = value => {
// 只有進行中狀態才能更改狀態
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 新增代碼:
// 成功態函數依次執行
this.onFulfilledCallbacks.forEach(fn => fn(this.value));
}
}
const reject = reason => {
// 只有進行中狀態才能更改狀態
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 新增代碼:
// 失敗態函數依次執行
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
try {
// 當即執行executor
// 把內部的resolve和reject傳入executor,用戶可調用resolve和reject
exector(resolve, reject);
} catch(e) {
// executor執行出錯,將錯誤內容reject拋出去
reject(e);
}
}
then(onFulfilled, onRejected) {
// then是微任務,這裏用setTimeout模擬
setTimeout(() => {
// 新增代碼:
if (this.status === PENDING) {
// 狀態是PENDING下執行
// 說明promise內部有異步代碼執行,還未改變狀態,添加到成功/失敗回調任務隊列便可
this.onFulfilledCallbacks.push(onFulfilled);
this.onRejectedCallbacks.push(onRejected);
}else if (this.status === FULFILLED) {
// FULFILLED狀態下才執行
onFulfilled(this.value);
} else if (this.status === REJECTED) {
// REJECTED狀態下才執行
onRejected(this.reason);
}
})
}
}
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000);
}).then(
res => console.log(res)
)
// 1
複製代碼
Promise
的一大優點就是支持鏈式調用,具體來講就是then
方法的具體實現,其實是返回了一個Promise
,須要注意的幾個點:
this
then
回調函數執行的返回值resolve
或reject
完善then
函數:
then(onFulfilled, onRejected) {
// 保存this
const self = this;
return new Promise((resolve, reject) => {
if (self.status === PENDING) {
self.onFulfilledCallbacks.push(() => {
// try捕獲錯誤
try {
// 模擬微任務
setTimeout(() => {
const result = onFulfilled(self.value);
// 分兩種狀況:
// 1. 回調函數返回值是Promise,執行then操做
// 2. 若是不是Promise,調用新Promise的resolve函數
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
});
self.onRejectedCallbacks.push(() => {
// 如下同理
try {
setTimeout(() => {
const result = onRejected(self.reason);
// 不一樣點:此時是reject
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
})
} else if (self.status === FULFILLED) {
setTimeout(() => {
try {
const result = onFulfilled(self.value);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
} catch(e) {
reject(e);
}
});
} else if (self.status === REJECT){
setTimeout(() => {
try {
const result = onRejected(self.error);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
} catch(e) {
reject(e);
}
})
}
})
}
複製代碼
Promise
支持值穿透:
let promsie = new Promise((resolve,reject)=>{
resolve(1)
})
.then(2)
.then(3)
.then(value => {
console.log(value)
})
// 1
複製代碼
then
參數指望是函數,傳入非函數則會發生值穿透。值傳透能夠理解爲,當傳入then的不是函數的時候,這個then是無效的。
原理上是當then中傳入的不算函數,則這個promise
返回上一個promise
的值,這就是發生值穿透的緣由,因此只須要對then
的兩個參數進行設置就好了:
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function'? onRejected:
reason => { throw new Error(reason instanceof Error ? reason.message:reason) }
複製代碼
完整的then函數代碼:
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function'? onRejected:
reason => { throw new Error(reason instanceof Error ? reason.message:reason) }
// 保存this
const self = this;
return new Promise((resolve, reject) => {
if (self.status === PENDING) {
self.onFulfilledCallbacks.push(() => {
// try捕獲錯誤
try {
// 模擬微任務
setTimeout(() => {
const result = onFulfilled(self.value);
// 分兩種狀況:
// 1. 回調函數返回值是Promise,執行then操做
// 2. 若是不是Promise,調用新Promise的resolve函數
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
});
self.onRejectedCallbacks.push(() => {
// 如下同理
try {
setTimeout(() => {
const result = onRejected(self.reason);
// 不一樣點:此時是reject
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
})
} else if (self.status === FULFILLED) {
try {
setTimeout(() => {
const result = onFulfilled(self.value);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
});
} catch(e) {
reject(e);
}
} else if (self.status === REJECTED){
try {
setTimeout(() => {
const result = onRejected(self.reason);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
}
});
}
複製代碼
Promise.prototype.catch
就是Promise.prototype.then(null, onRejected)
的別名,因此實現就很簡單了:
catch(onRejected) {
return this.then(null, onRejected);
}
複製代碼
這裏就不考慮參數是thenable
對象了,那麼參數有兩種狀況:
Promise
實例Promise
實例static resolve(value) {
if (value instanceof Promise) {
// 若是是Promise實例,直接返回
return value;
} else {
// 若是不是Promise實例,返回一個新的Promise對象,狀態爲FULFILLED
return new Promise((resolve, reject) => resolve(value));
}
}
複製代碼
Promise.reject
也會返回一個Promise實例,狀態爲REJECTED
。
與Promise.resolve
不一樣的是,Promise.reject
方法的參數會原封不動地做爲reject
的參數
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
複製代碼
返回一個promise對象,只有當全部promise都成功時返回的promise狀態才成功,須要注意的點是:
FULFILLED
,返回的promise狀態才變爲FULFILLED
。REJECTED
,返回的promise狀態就變爲REJECTED
。Promise.resolve()
處理。static all(promiseArr) {
const len = promiseArr.length;
const values = new Array(len);
// 記錄已經成功執行的promise個數
let count = 0;
return new Promise((resolve, reject) => {
for (let i = 0; i < len; i++) {
// Promise.resolve()處理,確保每個都是promise實例
Promise.resolve(promiseArr[i]).then(
val => {
values[i] = val;
count++;
// 若是所有執行完,返回promise的狀態就能夠改變了
if (count === len) resolve(values);
},
err => reject(err),
);
}
})
}
複製代碼
Promise.race()
實現就比較簡單了:
static race(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(p => {
Promise.resolve(p).then(
val => resolve(val),
err => reject(err),
)
})
})
}
複製代碼
// 模擬實現Promise
// Promise利用三大手段解決回調地獄:
// 1. 回調函數延遲綁定
// 2. 返回值穿透
// 3. 錯誤冒泡
// 定義三種狀態
const PENDING = 'PENDING'; // 進行中
const FULFILLED = 'FULFILLED'; // 已成功
const REJECTED = 'REJECTED'; // 已失敗
class Promise {
constructor(exector) {
// 初始化狀態
this.status = PENDING;
// 將成功、失敗結果放在this上,便於then、catch訪問
this.value = undefined;
this.reason = undefined;
// 成功態回調函數隊列
this.onFulfilledCallbacks = [];
// 失敗態回調函數隊列
this.onRejectedCallbacks = [];
const resolve = value => {
// 只有進行中狀態才能更改狀態
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 成功態函數依次執行
this.onFulfilledCallbacks.forEach(fn => fn(this.value));
}
}
const reject = reason => {
// 只有進行中狀態才能更改狀態
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 失敗態函數依次執行
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
try {
// 當即執行executor
// 把內部的resolve和reject傳入executor,用戶可調用resolve和reject
exector(resolve, reject);
} catch(e) {
// executor執行出錯,將錯誤內容reject拋出去
reject(e);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function'? onRejected :
reason => { throw new Error(reason instanceof Error ? reason.message : reason) }
// 保存this
const self = this;
return new Promise((resolve, reject) => {
if (self.status === PENDING) {
self.onFulfilledCallbacks.push(() => {
// try捕獲錯誤
try {
// 模擬微任務
setTimeout(() => {
const result = onFulfilled(self.value);
// 分兩種狀況:
// 1. 回調函數返回值是Promise,執行then操做
// 2. 若是不是Promise,調用新Promise的resolve函數
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
});
self.onRejectedCallbacks.push(() => {
// 如下同理
try {
setTimeout(() => {
const result = onRejected(self.reason);
// 不一樣點:此時是reject
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
})
} else if (self.status === FULFILLED) {
try {
setTimeout(() => {
const result = onFulfilled(self.value);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
});
} catch(e) {
reject(e);
}
} else if (self.status === REJECTED) {
try {
setTimeout(() => {
const result = onRejected(self.reason);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
static resolve(value) {
if (value instanceof Promise) {
// 若是是Promise實例,直接返回
return value;
} else {
// 若是不是Promise實例,返回一個新的Promise對象,狀態爲FULFILLED
return new Promise((resolve, reject) => resolve(value));
}
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
static all(promiseArr) {
const len = promiseArr.length;
const values = new Array(len);
// 記錄已經成功執行的promise個數
let count = 0;
return new Promise((resolve, reject) => {
for (let i = 0; i < len; i++) {
// Promise.resolve()處理,確保每個都是promise實例
Promise.resolve(promiseArr[i]).then(
val => {
values[i] = val;
count++;
// 若是所有執行完,返回promise的狀態就能夠改變了
if (count === len) resolve(values);
},
err => reject(err),
);
}
})
}
static race(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(p => {
Promise.resolve(p).then(
val => resolve(val),
err => reject(err),
)
})
})
}
}
複製代碼