手寫 Promise
- 2022 年 5 月 20 日
- 筆記
- javascript
就是干
ECMAscript 6 原生提供了 Promise 對象。Promise 對象代表了未來將要發生的事件,用來傳遞異步操作的消息。
接下來用分塊、分步驟、加註釋來一步一步,實現手寫 Promise。
一、實現 Promise 的基本使用
- Promise 就是一個類在執行這個類的時候需要傳遞一個執行器進去,執行器會立即執行
- Promise 中有三種狀態 分別為 等待(pending) 成功(fulfilled)失敗(rejected)
一旦狀態確定就不可更改
pending -> fulfilled
pending -> rejected - resolve 和 reject 函數是用來更改狀態的
resolve: fulfilled
reject: rejected - then 方法內部做的事情就判斷狀態,如果狀態成功就調用成功的回調函數,如果狀態失敗就調用失敗的回調函數 then 方法是被定義在原型對象上面
- then 成功和失敗之後都會有一個對應的成功值和失敗值
先定義三個常量分別代表以下狀態
- const PENDING = ‘pending’ // 等待
- const FULFILLED = ‘fulfilled’ // 成功
- const REJECTED = ‘rejected’ // 失敗
簡單講了一下基本概念,下面貼代碼:
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING // 默認值 等待
value = undefined // 成功之後的值
reason = undefined // 失敗後的值
// resolve,reject 箭頭函數 是為了讓這個函數內部 this 指向這個類的實例對象,也就是 promise
resolve = (value) => {
// 如果狀態不是等待 阻止程序繼續執行
if (this.status !== PENDING) return
// 將狀態改為成功
this.status = FULFILLED
// 保存成功之後的值
this.value = value
}
reject = (reason) => {
// 如果狀態不是等待 阻止程序繼續執行
if (this.status !== PENDING) return
// 將狀態改為成功
this.status = REJECTED
// 保存失敗後的原因
this.reason = reason
}
then(successCallback, failCallback) {
// 判斷狀態
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
}
}
}
二、實現 Promise 的異步情況
- 處理異步情況 和多個 promise.then 這裡指的是多個 promise 的使用,並不是鏈式調用
class MyPromise {
constructor(executor) {
executor(this.resolve, this.reject)
}
status = PENDING // 默認值 等待
value = undefined // 成功之後的值
reason = undefined // 失敗後的值
successCallback = [] // 成功回調,存儲多個函數,鏈式調用
failCallback = [] // 失敗回調,存儲多個函數,鏈式調用
// resolve,reject 箭頭函數 是為了讓這個函數內部 this 指向這個類的實例對象,也就是 promise
resolve = (value) => {
// 如果狀態不是等待 阻止程序繼續執行
if (this.status !== PENDING) return
// 將狀態改為成功
this.status = FULFILLED
// 保存成功之後的值
this.value = value
// 循環存儲的成功回調函數長度,然後依次執行
while (this.successCallback.length) this.successCallback.shift()(this.value)
}
reject = (reason) => {
// 如果狀態不是等待 阻止程序繼續執行
if (this.status !== PENDING) return
// 將狀態改為成功
this.status = REJECTED
// 保存失敗後的原因
this.reason = reason
// 循環存儲的失敗回調函數長度,然後依次執行
while (this.failCallback.length) this.failCallback.shift()(this.reason)
}
then(successCallback, failCallback) {
// 判斷狀態
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
} else {
// 因為等待期間你不知道後面執行的是成功的回調還是失敗的回調
// 所以將成功回調和失敗回調存儲起來
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
}
三、實現 Promise 的鏈式調用及捕獲錯誤信息
- then 方法的鏈式調用,以及避免 promise 對象自已返回自己 形成循環
- 將 then 參數變成可選參數
- 捕獲執行器錯誤信息,以及使用 then 時捕獲錯誤信息
class MyPromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (e) {
this.reject(e)
}
}
status = PENDING // 默認值 等待
value = undefined // 成功之後的值
reason = undefined // 失敗後的值
successCallback = [] // 成功回調,存儲多個函數,鏈式調用
failCallback = [] // 失敗回調,存儲多個函數,鏈式調用
// resolve,reject 箭頭函數 是為了讓這個函數內部 this 指向這個類的實例對象,也就是 promise
resolve = (value) => {
// 如果狀態不是等待 阻止程序繼續執行
if (this.status !== PENDING) return
// 將狀態改為成功
this.status = FULFILLED
// 保存成功之後的值
this.value = value
// 循環存儲的成功回調函數長度,然後依次執行
while (this.successCallback.length) this.successCallback.shift()()
}
reject = (reason) => {
// 如果狀態不是等待 阻止程序繼續執行
if (this.status !== PENDING) return
// 將狀態改為成功
this.status = REJECTED
// 保存失敗後的原因
this.reason = reason
// 循環存儲的失敗回調函數長度,然後依次執行
while (this.failCallback.length) this.failCallback.shift()()
}
then(successCallback, failCallback) {
// 判斷,變成可選參數,假設第一個,第二個 .then 不傳參數,第三個傳參,一樣能拿到數據
successCallback = successCallback ? successCallback : value => value
failCallback = failCallback ? failCallback : reason => { throw reason }
// 為了鏈式調用返回一個新的 promise 對象
let newPromise = new MyPromise((resolve, reject) => {
// 判斷狀態
if (this.status === FULFILLED) {
// 這裡用計時器的原因是為了異步調用,因為需要等 newPromise 執行完畢後,才可以把他當參數傳遞給 resolvePromise 這個函數
setTimeout(() => {
try {
let x = successCallback(this.value)
// 存儲上一個成功回調的返回值,傳遞給下一個 .then
// 判斷 x 的值是普通值還是 promise 對象
// 如果是普通值,直接調用 resolve
// 如果是 promise 對象,查看 promise 對象返回的結果
// 再根據 promise 對象返回的結果,決定調用 resolve 還是 reject
// 聲明一個方法,進行操作
resolvePromise(newPromise, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = failCallback(this.reason)
// 存儲上一個成功回調的返回值,傳遞給下一個 .then
// 判斷 x 的值是普通值還是 promise 對象
// 如果是普通值,直接調用 resolve
// 如果是 promise 對象,查看 promise 對象返回的結果
// 再根據 promise 對象返回的結果,決定調用 resolve 還是 reject
// 聲明一個方法,進行操作
resolvePromise(newPromise, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
} else {
// 因為等待期間你不知道後面執行的是成功的回調還是失敗的回調
// 所以將成功回調和失敗回調存儲起來
this.successCallback.push(() => {
setTimeout(() => {
try {
let x = successCallback(this.value)
// 存儲上一個成功回調的返回值,傳遞給下一個 .then
// 判斷 x 的值是普通值還是 promise 對象
// 如果是普通值,直接調用 resolve
// 如果是 promise 對象,查看 promise 對象返回的結果
// 再根據 promise 對象返回的結果,決定調用 resolve 還是 reject
// 聲明一個方法,進行操作
resolvePromise(newPromise, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
this.failCallback.push(() => {
setTimeout(() => {
try {
let x = failCallback(this.reason)
// 存儲上一個成功回調的返回值,傳遞給下一個 .then
// 判斷 x 的值是普通值還是 promise 對象
// 如果是普通值,直接調用 resolve
// 如果是 promise 對象,查看 promise 對象返回的結果
// 再根據 promise 對象返回的結果,決定調用 resolve 還是 reject
// 聲明一個方法,進行操作
resolvePromise(newPromise, x, resolve, reject)
} catch (e) {
reject(e)
}
}, 0)
})
}
})
return newPromise
}
}
function resolvePromise(newPromise, x, resolve, reject) {
if (newPromise === x) {
return reject(new TypeError('出錯了大爺,不要自己調自己'))
}
if (x instanceof MyPromise) {
// 判斷是否是實例對象就行
// 如果成功就調用 resolve,如果失敗就調用 reject
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
四、實現 Promise.all()
- 實現 Promise.all()
- 實現 Promise.resolve()
- 實現 finally()
因為下述代碼只是新增了幾個靜態方法,剩餘代碼同第三步一是樣的,所以只展示新增的變化
class MyPromise {
// all
static all (array) {
// 解決異步並發問題,並且可以按照順序調用,並且該方法是靜態方法
let result = []
let index = 0
return new MyPromise((resolve, reject) => {
function addData (key, value) {
result[key] = value
index++
if (index === array.length) {
// 是為等所有的異步操作執行完畢
resolve(result)
}
}
for (let i = 0; i < array.length; i++) {
// 循環 all 數組裏面的值
let current = array[i]
if (current instanceof MyPromise) {
// promise 對象
current.then(value => addData(i, value), reason => reject(reason))
} else {
// 普通值
addData(i, array[i])
}
}
})
}
// resolve
static resolve (value) {
if (value instanceof MyPromise) return value
return new MyPromise ((resolve) => resolve(value))
}
// finally
finally (callback) {
return this.then((value) => {
return MyPromise.resolve(callback()).then(() => value)
}, (reason) => {
return MyPromise.resolve(callback()).then(() => {throw reason})
})
}
catch (failCallback) {
return this.then(undefined, failCallback)
}
}