一個遵循PromiseA+規範的函數,個人認為解決了callback異步回調地獄的問題,注意是callback方式的回調地獄,promise本身也可以存在回調地獄,需配合ES7特性async、await才能做到完全解決回調地獄。
Promise主要特點
- Promise 會有三種狀态,「進⾏中」「已完成」和「已拒絕」,進⾏中狀态可以更改為已完成或已拒絕,已經更改過狀态後⽆法繼續更改(例如從已完成改為已拒絕)。
- Promise 構造之後需要傳⼊⼀個函數,它接受兩個參數,執⾏第⼀個參數之後就會改變目前 promise 為「已完成」狀态,執⾏ 第⼆個參數之後就會變為「已拒絕」狀态。
- 通過
⽅法,即可在上⼀個 promise 達到已完成 時繼續執⾏下⼀個函數或 promise。同時通過 resolve 或 reject 時傳⼊參數,即可給下⼀個函數或 promise 傳⼊初始值。.then
- 已拒絕的 promise,後續可以通過 .catch ⽅法或是 .then ⽅法的第⼆個參數或是 try catch 進⾏捕獲。
根據特性梳理結構
class simplePromise {
constructor(handleFn) { // 構造時傳入一個函數
this.status = 'pending' // 有狀态控制
handleFn(resolve, reject) // 該函數接收兩個參數
}
then() { // 它有一個then方法
return new simplePromise(() => { // 傳回的是promise執行個體then才得以串聯起來
if (this.status === 'pending') { // 狀态一旦改變就不可修改
// TODO
}
})
}
// 一些常用的靜态方法
catch() {}
resolve() {}
reject() {}
}
實作resolve
這是Promise的第一個關鍵點,主要在于邏輯的判斷,如何将then串聯起來,以及最後收集依賴
class SimplePromise {
constructor(handleFn) {
this.status = 'pending' // 标記狀态
this.fulfilledList = [] // 任務隊列
handleFn(params { })
}
then(onFulfilled, onRejected) { // then()接收兩個函數
// 傳回一個Promise
return new SimplePromise((onNextFulfilled, onNextRejected) => {
function finalFn(params) {
// 如果不是函數,跳過執行下一步
if (typeof onFulfilled !== 'function') {
onNextFulfilled(params)
} else {
// 如果是函數,先接收其傳回結果
const res = onFulfilled(params)
// 判斷是否為Promise,也可以判斷有沒有then方法typeof res.then === 'function'
if (res && res instanceof SimplePromise) {
res.then(onNextFulfilled) // 繼續執行Promise
} else {
onNextFulfilled(res) // 不是Promise繼續執行下一步
}
}
}
if (this.status === 'pending') {
this.fulfilledList.push(finalFn) // 收集依賴
第二個關鍵點在于,使用者傳入的函數,在構造時會立即執行,但收集依賴是在then方法中,是以需要将傳入的函數放到異步隊列中去執行
class SimplePromise {
constructor(handleFn) {
this.status = 'pending' // 标記狀态
this.fulfilledList = [] // 任務隊列
handleFn((val) => {
setTimeout(() => { // 抛進異步隊列,保證不會立即執行
if (this.status !== 'pending') { return } // 狀态一旦确定再無法變更
this.status = 'fulfilled' // 執行就改變狀态
this.fulfilledList.forEach(fn fn(val))// 開始執行函數
this.fulfilledList = [] // 釋放
}, 0)
})
}
then(onFulfilled, onRejected) { ..... }
catch() { }
resolve() { }
reject() { }
}
- 接下來将reject也實作進去,handleFn為使用者傳入的函數,其接收兩個參數resolve和reject,我們分别執行了兩個隊列裡收集的任務。
- 而then中則建立了一個工廠函數,用于生成兩種收集依賴的方法。
完整代碼
class SimplePromise {
constructor(handleFn) {
this.status = 'pending' // 标記狀态
this.fulfilledList = [] // 任務隊列
this.rejectedList = []
handleFn(this.trigger.bind(this, 'fulfilled'), this.trigger.bind(this, 'rejected'))
}
trigger(status, val) {
setTimeout(() => { // 抛進異步隊列,保證不會立即執行
if (this.status !== 'pending') return; // 狀态一旦确定再無法變更
this.status = status // 狀态變更
this[`${status}List`].forEach(fn fn(val)) // 開始執行函數
}, 0)
}
then(onFulfilled, onRejected) {
return new SimplePromise((onNextFulfilled, onNextRejected) => {
function createFinalFn(prev, next) {
return function (params) {
if (typeof prev !== 'function') {
next(params)
} else {
const res = prev(params)
res && res instanceof SimplePromise ? res.then(next) : next(res)
}
}
}
if (this.status === 'pending') {
this.fulfilledList.push(createFinalFn(onFulfilled, onNextFulfilled))
this.rejectedList.push(createFinalFn(onRejected, onNextRejected))
}
})
}
catch(onRejected) { // 傳回reject
return this.then(null, onRejected)
}
static resolve(val) { // 直接成功執行的結果
return new SimplePromise(resolve resolve(val))
}
static reject(val) { // 暴露出失敗結果,或許可以用來做Promise的中斷
return new SimplePromise((resolve, reject) => reject(val))
}
}
至此大概40行的代碼實作了一個簡單的Promise,寫個例子測試下
const p = new SimplePromise((resolve, reject) => {
resolve('success');
}).then(res {
console.log('ok', res)
}, err {
console.log('no', err)
})
// ok success
注:本例中實作的簡單Promise無法跑通全部官方測試用例,隻是對實作規範中的核心部分進行一次編寫練習。
主要了解以上兩個關鍵點其實就了解了Promise的核心部分,面試中可應對大部分Promise原理考察,如果面試官要你用手寫完整的Promise實作,那你應該反問他貴公司是不是從來不使用電腦辦公。
最後用上我們手寫的SimplePromise來運作一道題加深對Promise的認識
var promise = new SimplePromise(function(resolve, reject){
setTimeout(function() {
resolve(1);
}, 1000)
})
promise.then(() => {
return SimplePromise.resolve(2);
}).then((n) => {
console.log('我是第一個:結果是',n)
});
promise.then(() => {
return 2
}).then((n) => {
console.log('我是第二個:結果是',n)
});
promise.then(2).then((n) => {
console.log('我是第三個:結果是',n)
});
我是第二個: 結果是2
我是第三個: 結果是1
我是第一個: 結果是2