在 promise 出現以前,異步編程由回調函數完成,很容易出現回調嵌套過多,也即常提到的「回調地獄」。回調地獄不只是可讀性差,維護起來也至關麻煩,若是某個環節出錯了,常常沒法準肯定位問題。編程
Promise 正是爲了解決這些問題而出現,鏈式調用解決了回調地獄的問題,它的錯誤傳播機制實現了統一的錯誤信息處理。能夠看一個 MDN 上的例子:promise
// 回調地獄
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
// Promise 方案
doSomething()
.then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
複製代碼
因爲 ES6 中的 Promise 聽從 Promise A+ 規範,接下來就根據其主要的幾個規則,開始一塊兒動手實現吧!異步
咱們先根據 Promise
的調用方式搭建它的構造函數:ide
class Promise {
constructor (excutor) {
this.value = null
this.reason = null
const resolve = (value) => {
this.value = value
}
const reject = (reason) => {
this.reason = reason
}
try {
excutor(resolve, reject)
} catch (reason) {
reject(reason)
}
}
}
複製代碼
每個 Promise 實例只能有三個狀態:pending
,fulfilled
,rejected
,且狀態之間的轉換隻能是 pending => fulfilled
或 pending => rejected
。咱們接着實現這個功能:異步編程
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class Promise {
constructor (excutor) {
this.value = null
this.reason = null
this.state = PENDING
const resolve = (value) => {
if (this.state === PENDING) {
this.value = value
this.state = FULFILLED
}
}
const reject = (reason) => {
if (this.state === PENDING) {
this.reason = reason
this.state = REJECTED
}
}
try {
excutor(resolve, reject)
} catch (reason) {
reject(reason)
}
}
}
複製代碼
then
方法Promise
中的 then
方法接受兩個函數做爲參數,分別是 Promise
成功和失敗的回調。因此在 then
方法中,先判斷 Promise
的狀態,若是是成功,就將 this.value
傳入回調並執行;若是失敗,將 this.reason
傳入回調並執行(因此須要全局的 value
和 reason
)。函數
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class Promise {
constructor (excutor) {
...
}
then (onFulfilled, onRejected) {
if (this.state === FULFILLED) {
onFullfilled(this.value)
} else {
onRejected(this.reason)
}
}
}
複製代碼
目前咱們的實現只是同步的,思考一下下面例子中 then
中的回調函數會不會執行?ui
let p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000)
})
// 回調會執行嗎?
p1.then((v) => {
console.log('success')
})
複製代碼
例子中的 resolve
函數是異步執行的,而 then
是同步執行的,也就是說 then
會先於 resolve
執行,那 then
執行的時候 Promise
的狀態仍是 Pending
。因此,then
中的回調函數是不會執行的。接下來咱們作一些修改來解決這個問題:this
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class Promise {
constructor (excutor) {
this.value = null
this.reason = null
this.state = PENDING
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
if (this.state === PENDING) {
this.value = value
this.state = FULFILLED
this.onFulfilledCallbacks.forEach(onFulfilledCallback => {
onFulfilledCallback()
})
}
}
const reject = (reason) => {
if (this.state === PENDING) {
this.reason = reason
this.state = REJECTED
this.onRejectedCallbacks.forEach(onRejectedCallback => {
onRejectedCallback()
})
}
}
try {
excutor(resolve, reject)
} catch (reason) {
reject(reason)
}
}
then (onFulfilled, onRejected) {
if (this.state === FULFILLED) {
this.onFulfilledCallbacks.push(() => {
onFulfilled(this.value)
})
} else {
this.onRejectedCallbacks.push(() => { onRejected(this.reason)
})
}
}
}
複製代碼
在 Promise A+ 規範中,then
函數是能夠鏈式調用的,也就是說 then
函數的返回值也是一個 Promise
,且這個 Promise
的 resolve
值是上一個 Promise
的 onFulfilled
函數的返回值,或 onRejected
函數的返回值。而若是上一個 Promise
的執行過程當中發生錯誤,那麼這個錯誤將被做爲返回的 Promise
的 ``onRejected 函數的參數傳入。spa
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class Promise {
...
resolvePromise (promise, x, resolve, reject) {
if (promise === x) {
reject(new TypeError('Chaining cycle'))
return
}
if (x instanceof APromise) {
if (x.state === PENDING) {
x.onFulfilledCallbacks.push(() => {
resolve(x.value)
})
} else if (x.state === FULFILLED) {
resolve(x.value)
} else if (x.state === REJECTED) {
reject(x.reason)
}
} else if (x && typeof x === 'object' || typeof x === 'function') {
let used
try {
let then = x.then
if (typeof then === 'function') {
then.call(x, y => {
if (used) return
used = true
this.resolvePromise(promise, y, resolve, reject)
}, r => {
if (used) return
used = true
reject(r)
})
} else {
if (used) return
used = true
resolve(x)
}
} catch (reason) {
if (used) return
used = true
reject(reason)
}
} else {
resolve(x)
}
}
then (onFulfilled, onRejected) {
const promise2 = new Promise((resolve, reject) => {
if (this.state === PENDING) {
this.onFulfilledCallbacks.push(() => {
try {
let x = onFulfilled(this.value)
this.resolvePromise(promise2, x, resolve, reject)
} catch (reason) {
reject(reason)
}
})
this.onRejectedCallbacks.push(() => {
if (typeof onRejected !== 'function') {
reject(this.reason)
} else {
try {
let x = onRejected(this.reason)
this.resolvePromise(promise2, x, resolve, reject)
} catch (reason) {
reject(reason)
}
}
})
} else if (this.state === FULFILLED) {
try {
let x = onFulfilled(this.value)
this.resolvePromise(promise2, x, resolve, reject)
} catch (reason) {
reject(reason)
}
} else {
try {
let x = onRejected(this.reason)
this.resolvePromise(promise2, x, resolve, reject)
} catch (reason) {
reject(reason)
}
}
})
return promise2
}
}
複製代碼
咱們已經知道 then
方法會返回一個 Promise
對象,且這個對象的 resolve
值是前一個 Promise
的 onFulfilled
函數的返回值,或 onRejected
函數的返回值。可是當返回值是 thenable
對象,或是 Promise
時,須要有特殊處理。具體的邏輯處理在 resolvePromise(promise, x, resolve, reject)
方法中,咱們接下來結合規範來看它的執行過程 :code
promise
和 x
是同一個對象,那拋出一個 Chaining cycle
的 TypeError
x
是一個 Promise
對象,那麼 promise
將使用 x
的狀態
x
處於 PENDING
狀態,promise
也將保持 PENDING
狀態直到 x
被 resolve
或 reject
x
處於 FULFILLED
狀態,用 x
的 value
來 resolve promise
x
處於 REJECTED
狀態,用一樣的 reason
來 reject promise
x
是一個對象或函數,將 then
指向 x.then
then
是一個函數,那調用它,並將 x
做爲它的 this
,以 resolvePromise
做爲第一個參數,rejectPromise
做爲第二個參數,且:
resolvePromise
以一個 y
值被調用了,則執行 resolvePromise(promise, x, resolve, reject)
resolvePromise
以一個 r
值被拒絕了,則執行 reject(r)
resolvePromise
和 rejectPromise
都被調用了,或者某個已被被屢次調用了,則首次發生的調用生效,其他全部調用都被忽略。then
調用過程當中發生了錯誤,若是 resolvePromise
和 rejectPromise
都沒被調用過,則 reject
這個錯誤;不然忽略這個錯誤then
不是函數,則以 x
來 resolve promise
reject promise
catch
的異常處理考慮這個場景:promise
被 reject
且後續的 then
方法沒有傳入 onRejected
處理函數,那麼根據 then
中的處理邏輯,第二個 promise
的 reason
就是前一個promise
的 reason
值。因此 throw
方法中只要調用 this.then(null, onRejected)
就能處理第一個 promise
未被處理的 reject
。
class Promise {
...
throw (onRejected) {
return this.then(null, onRejected)
}
}
複製代碼