就是幹
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)
}
}