Promise能夠看做一個異步操做的佔位符,表示一個異步操做的最終狀態(完成或失敗),以及其返回的值,是目前流行Javascript異步編程解決方案之一。git
網上關於Promise用法的文章汗牛充棟,做者在本文也不贅述了。Promise用久了不免會對其內部運行原理感到好奇,做者曾試着遵循Promise/A+規範本身寫一個promise的補丁,無奈野心被能力實力打臉,冷靜以後 去github上找promise的polyfill學習觀摩,promise-polyfill做爲一個輕量級、設計巧妙的polyfill讓做者大開眼界,仔細觀摩學習以後,寫成本篇小記與你們分享,錯漏之處懇求指點。github
做者將不會按照promise-polyfill的源碼一行行進行講解,而爲了閱讀方便將使用ES6/7的語法從新搭好一個框架,慢慢往裏面塞promise-polyfill核心代碼面試
咱們將搭好下面的代碼架構,先實現除了constructor、then的其餘方法找找感受,而後慢慢往裏加promise-polyfill的核心代碼,不耐煩的同窗能夠直接跳到下文 核心 部分閱讀編程
class Promise {
constructor (executor) { ... }
then (onFulfilled, onFulfilled) { ... }
catch (onRejected) { ... }
finally(callback) { ... }
static resolve(value) { ... }
static reject(reason) { ... }
static all(iterable) { ... }
static race(iterable) { ... }
}
複製代碼
先僞裝已經實現了 constructor、then 的核心功能c#
/** * catch很簡單,就是then(null, onRejected)的語法糖 */
catch (onRejected) {
return this.then(null, onRejected)
}
複製代碼
/*** ** finally老是執行回調 ***/
finally (callback) {
return this.then(
function(value) {
return Promise.resolve(callback()).then(() => value)
},
function(reason) {
return Promise.resolve(callback()).then(() => Promise.reject(reason))
}
)
}
// tips
prom.finally(() => { «statements» })
等於
prom.then(result => {
«statements»
return result
}, error => {
«statements»
throw error
})
複製代碼
/** * 靜態resolve方法返回一個Promise對象 */
static resolve (value) {
if (value && typeof value === 'object' && value.constructor === Promise) {
return value
}
return new Promise(function(resolve, reject) {
resolve(value)
})
}
複製代碼
static reject (reason) {
return new Promise(function (resolve, reject) {
reject(reason)
})
}
複製代碼
/** **** 簡單起見,並未按照promise-polyfill的all方法,面試中也常常出現要求實現一個Promise.all方法 **/
static all (iterable) {
return new Promise((resolve, reject) => {
let count = 0, result = [], len = iterable.length
if (len === 0) {
resolve(result)
}
function resolver (i) {
return function (x) {
result[i] = x
count++
if (count === i) {
resolve(result)
}
}
}
for(let i = 0; i < len; i++) {
Promise.resolve(iterable[i]).then(resolver(i), reject)
}
})
}
複製代碼
/** **** 誰的異步時間短,誰先處理,其他忽略 ***/
static race(iterable) {
return new Promise((resolve, reject) => {
for (let i = 0, len = iterable.length ; i < len; i++ ) {
Promise.resolve(iterable[i]).then(resolve, reject)
}
})
}
複製代碼
爲了不沒必要要的干擾,省略代碼將以 @code_{function_name}_{number}標號。部分優化、兼容代碼將省略,例如對setTimeout的優化,將直接使用setTimeout,並不會影響代碼邏輯promise
/** * @constructor 初始化代碼 * 1. 初始化代碼進行常規的類型檢查和屬性賦值 * 2. @_state狀態 * penging <===> 0 * fulfilled <===> 1 * rejected <===> 2 * <Promise> <===> 3 (自定義的state,用於取<Promise>內部的值,在下文詳解) * 3.@_handled標記當前Promise實例的結果是否被處理過,初始化爲false * 3.@_deferreds 存放在then中的註冊的函數,元素形式{onFulfilled, onRejected,promise} * 4.@doResolve 詳見下文 **/
constructor (executor) {
if (!(this instanceof Promise))
throw new TypeError('Promises must be constructed via new')
if (typeof fn !== 'function') throw new TypeError('not a function')
/** @type {!number} */
this._state = 0
/** @type {!boolean} */
this._handled = false
/** @type {Promise|undefined} */
this._value = undefined
/** @type {!Array<!Function>} */
this._deferreds = []
doResolve(fn, this)
}
/*** ** @doResolve分發self給resolve和reject進行狀態轉換 ** @done確保當前實例的狀態由penging=>fulfilled 或者 penging=>rejected只發生一次 ** @resolve、 reject 全局方法,處理狀態轉換 **/
function doResolve (fn, self) {
var done = false
try {
fn(
function (value) {
if (done) return
done = true
resolve(self, value)
},
function (reason) {
if (done) return
done = true
reject(self, reason)
}
)
} catch (ex) {
if (done) return
done = true
reject(self, ex)
}
}
複製代碼
executor執行完同步代碼,緊接着將在then中註冊方法onFulfilled、onRejected掛載到一個deferred對象上架構
// 空函數
const noop = function() {}
/** ** 1. 先建立一個Promise實例prom ** 2. new Handler(onFulfilled, onRejected, prom) 建立一個deferred對象掛載then上註冊的方法和 下個promise實例prom <deferred: {onFulfilled, onRejected, promise}> ** 3. handle在此將 deferred加入到當前promise實例的 _deferreds隊列上 ** 4. 返回Promise的實例prom,供後面的then、catch、繼續註冊方法 ** 5. prom不會在乎第一個promise是成功仍是失敗(以此類推)。 ** prom在乎第一個promise的處理函數(onFulfilled,onRejected)( 無論成功仍是失敗) ***/
then(onFulfilled, onRejected) {
let prom = new this.constructor(noop)
handle(this, new Handler(onFulfilled, onRejected, prom))
return prom
}
/** *** @Handler 建立一個deferred <deferred: {onFulfilled, onRejected, promise}> ** onFulfilled, onRejected不是函數則賦值爲null,方便在handle中統一處理 **/
function Handler (onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
this.onRejected = typeof onRejected === 'function' ? onRejected : null
this.promise = promise
}
/*** *** then方法中調用handle就只是爲了將deferred添加到當前Promise實例的_deferreds上,等待最後處理Promise的返回值 ***/
function handle (self, deferred) {
// ...@code_handle_1
if (self._state === 0) {
self._deferreds.push(deferred)
return
}
// ...@code_handle_2
}
複製代碼
咱們先將假設Promise的狀態已發生改變,如何改變的先不表。promise-polyfill把結果統一在finale中處理app
/*** **** @function finale *** 1. 當前實例在rejected狀態且未註冊deferred,註冊一個定時器在下個事件循環中檢查Promise的值是否被調用過 *** 2. 循環調用調用handle,用當前實例的_deferreds隊列中註冊的方法處理promise的返回值 *** 3. 釋放當前實例的_deferreds的空間 ***/
function finale (self) {
if (self._state === 2 && self._deferreds.length === 0) {
setTimeout(function () { // 原代碼有優化,此處代碼改動,但邏輯未變
if (!self._handled) {
if (typeof console !== 'undefined' && console) {
console.warn('Possible Unhandled Promise Rejection:', self.__value)// eslint-disable-line no-console
}
}
})
}
for (var i = 0, len = self._deferreds.length; i < len; i++) {
handle(self, self._deferreds[i])
}
self._deferreds = null
}
/** ** @function handle ** 1. while循環檢查當前實例的值是不是一個Promise實例,self = self._value直到self的值不是promise ** 2. self._state === 0,主要用在then方法中使用在添加deferred對象 ** 3. 標識當前promise實例已調用 ** 4. 啓動一個定時器,註冊方法在下個循環中處理當前promise結果 **/
function handle (self, deferred) {
while (self._state === 3) {
self = self._value
}
if (self._state === 0) {
self._deferreds.push(deferred)
return
}
self._handled = true
setTimeout(function () {
// 根據當前promise狀態,取掛載在deferred對象上的對應方法(在then方法中註冊的方法)
var cb = self._state === 1 ? deferredeferred.onFulfilled : deferred.onRejected
// 若是cb==null,則表示未在then中註冊方法,直接根據當前promise的狀態,調用resolve或reject,將當前值放到下一個then註冊的方法中,退出當前調用棧
if (cb === null) {
(self._state === 1 ? resolve : reject)(deferred.promise, self._value)
return
}
var ret
try {
// 由then中註冊的方法調用當前promise的值,返回值放入ret
ret = cb(self._value)
} catch (e) {
// 若是有異常將異常結果做爲當前實例then註冊方法的值傳入下個promise供then註冊的onRejected方法調用
reject(deferred.promise, e)
return
}
// 將ret做爲當前promise實例then方法中的值傳入下個promise的then註冊的onFulfilled方法調用
resolve(deferred.promise, ret)
})
}
複製代碼
非Promise靜態方法,用做處理Promise的狀態轉移框架
/** ******@function resolve ** 處理 penging ===> fulfilled/rejected的狀態轉移 ** 1. 檢查Promise實例是否resolve調用自身,報錯 轉rejected ** 2. 檢查newValue返回值是不是一個Promse實例,設置私有狀態3(最後將在handle中解析成非promise值),傳遞給finale進一步處理 ** 3. 檢查newValue是不是一個thenable類型,調用doResolve ** 4. 非Promise的值,state設爲1,傳遞給finale進一步處理 ***/
function resolve (self, newValue) {
try {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
// 對應規範2.3.1: 若是promise(self)和x(newValue)引用同一個對象,請以promisea TypeError爲理由拒絕。
if (newValue === self) { throw new TypeError('A promise cannot be resolved with itself.') }
if (
newValue &&
(typeof newValue === 'object' || typeof newValue === 'function')
) {
var then = newValue.then
if (newValue instanceof Promise) {
self._state = 3
self._value = newValue
finale(self)
return
} else if (typeof then === 'function') {
/** ** 對應規範 2.3.3.3 若是then是函數,請使用x as this,first參數resolvePromise和第二個參數調用它rejectPromise **/
doResolve(bind(then, newValue), self)
return
}
}
self._state = 1
self._value = newValue
finale(self)
} catch (e) {
reject(self, e)
}
}
/** ** Polyfill for Function.prototype.bind **/
function bind (fn, thisArg) {
return function () {
fn.apply(thisArg, arguments)
}
}
/*** * 修改狀態,傳遞給finale進一步處理 ***/
function reject (self, newValue) {
self._state = 2
self._value = newValue
finale(self)
}
複製代碼
總算水完了,看着好像也沒啥乾貨,錯漏之處懇求指點。異步