very-simple-promise,包含了不完善的單元測試❤️.react
代碼君的自白git
這篇的文章主要參考了上面的博客,謝謝他的幫助🙏。github
Promises/A+規範, 下面👇是對規範的內容的部分翻譯。英文比較爛,不喜歡的能夠不看。c#
承諾必須知足三種狀態, pending(等處理), fulfilled(履行), rejected(拒絕)數組
promise處於pending時promise
promise處於fulfilled時異步
promise處於rejected時函數
promise的then方法接受兩個參數源碼分析
promise.then(onFulfilled, onRejected)
onFulfilled和onRejected都是可選參數post
onFulfilled若是是一個函數
onRejected若是是一個函數
then能夠在同一個promise屢次調用
promise2 = promise1.then(onFulfilled, onRejected);
then必須返回promise
[[Resolve]](promise, x)須要遵循如下規範
若是x是Promise
若是x是對象或者函數
若是then是函數🤔️
若是then拋出了錯誤
onFulfilled和onRejected方法應當是異步執行,且應該在then方法被調用的那一輪事件循環以後的微任務執行棧中執行。能夠使用宏任務macrotask和微任務microtask機制實現。也就是說宏任務完成後(Promise的then調用後),會清空微任務隊列(執行onFulfilled或者onRejected)。
macrotask包含:script(總體代碼)、setTimeout、setInterval、I/O、UI交互事件、postMessage 、requestAnimationFrame 、MessageChannel、etImmediate(Node.js 環境)
microtask包含:Promise.then、setImmediate、MutaionObserver、process.nextTick(Node.js 環境)
下面👇是一個關於事件循環的例子🌰
Promise中的參數函數,應當是當即調用的。咱們這時先執行了resolve(1), 這會將外層的Promise的狀態置爲fulfilled態。可是這時外層的then尚未執行。
咱們接下來執行了Promise.resolve().then(() => console.log(2)), 咱們將then的onFulfilled,push到了macrotask隊列中,會在宏任務執行完成後清空微任務。
執行外層Promise的then, onFulfilled, push到了macrotask隊列中。
接着執行console.log(3), 而後清空macrotask隊列,執行2,1(隊列先進先出)
const pending = 0 const fulfilled = 1 const rejected = 2 function reject (promise, result) {} function resolve (promise, reason) {} export default class Promise { constructor (fn) { this.fn = fn // Promise的狀態, 初始狀態爲pending this._state = pending // Promise的fulfilled態的緣由,或者Promise的rejected態的理由 this._value = null // 存儲then的隊列 this._tasks = [] // 建立Promise對象後,當即執行fn this.init() } init () { try { // 執行fn, 傳入包裝後的resolve,reject // reject,resolve會修改promise的狀態 this.fn( (result) => resolve(this, result), (reason) => reject(this, reason) ) } catch (error) { reject(this) } } }
根據規範2.2.6: then may be called multiple times on the same promisethen能夠在同一個promise中屢次調用。因此咱們用一個數組存儲then的結果。
根據規範2.2.1: A promise’s then method accepts two arguments. Both onFulfilled and onRejected are optional arguments。then接受兩個參數, 兩個參數可選。
根據規範2.2.1.1, 2.2.1.2: If onFulfilled is not a function, it must be ignored. If onRejected is not a function, it must be ignored.。onFulfilled, onRejected必須是函數負責會被忽略。
根據規範2.2.2, 2.2.3, onFulfilled必須在狀態爲fulfilled調用, onRejected必須在狀態爲rejected調用
根據規範2.2.7: then must return a promise。then必須返回一個promise。
🤔️ 爲何不能返回當前的promise?由於根據規範2.1.2, 2.1.3, promise被修改狀態後,不能再次修改狀態。咱們若是須要執行then的callback須要修改promise的狀態。
咱們使用task封裝📦then的回調以及then返回的promise。
class Task { constructor (onFulfilled, onRejected, promise) { if (typeof onFulfilled !== 'function') { onFulfilled = null } if (typeof onRejected !== 'function') { onRejected = null } this.onFulfilled = onFulfilled this.onRejected = onRejected this.promise = promise } } class Promise { // ... then (onFulfilled, onRejected) { let nextPromise = new Promise(function () {}) let task = new Task(onFulfilled, onRejected, nextPromise) if (this._state === pending) { this._tasks.push(task) } else { handlePromise(this, task) } // 返回新的promise return nextPromise } }
catch函數一樣會返回新的promise, 可是在建立task時,咱們不會傳入onFulfilled參數。因此當promise當爲fulfilled態,雖然catch的回調一樣存放_task中,可是因爲callback爲null, 在handlePromise中會向下穿透。
class Promise { // ... catch (onRejected) { let nextPromise = new Promise(function () {}) // onFulfilled設置爲null let task = new Task(null, onRejected, nextPromise) if (this._state === pending) { this._tasks.push(task) } else { handlePromise(this, task) } // 返回新的promise return nextPromise } }
finally方法用於指定無論Promise對象最後狀態如何,都會執行的操做。
咱們不管promise的狀態如何,都在promise的最後面添加then,並傳入了onFulfilled, onRejected兩個回調。在回調中,咱們執行finally的callback參數。這樣不管以前的promise是fulfilled態,仍是rejected態,都會執行finally添加的參數。
class Promise { // ... finally (callback) { // this指向調用finally的對象 const self = this // 向Promise鏈中添加then,不管,promise是resolve態仍是reject態,都會執行callback // 而且會經過then,繼續將result或reason向下傳遞 return self.then( result => Promise.resolve(callback()).then(_ => result), reason => Promise.resolve(callback()).then(_ => { throw reason }) ) } }
resolve用來修改promise狀態,將promise狀態設置爲fulfilled態, 並執行then的onFulfilled回調
根據規範2.3.1: If promise and x refer to the same object, reject promise with a TypeError as the reason.若是promise與x相等,咱們使用TypeError的錯誤拒絕promise
根據規範2.3.2。若是result是promise,而且處於pending態,promise須要保持pending態,直到result被執行和拒絕後,咱們使用result的狀態履行或者拒絕promise。若是result是promise,而且處於fulfilled或rejected態,咱們使用result的狀態拒絕或者履行promise。
根據規範2.3.3, 咱們判斷result是否爲一個Object。若是result爲Object, 咱們則取出它的then的屬性, 判斷then屬性是否爲Function, 若是then爲Function, 咱們設置then的做用域的this指向result, 咱們傳入resolvePromise, rejectPromise做爲參數。
根據規範2.3.4: If x is not an object or function, fulfill promise with x, 若是x不是函數或者對象,咱們用result結果做爲參數執行promise。
function resolve (promise, result) { if (promise === result) { throw reject(promise, new TypeError('promise and x refer to the same object')) } if (isPromise(result)) { if (result._state === pending) { result._tasks.concat(promise._tasks) } else if (result._state === fulfilled || result._state === rejected) { let task while (task = promise._tasks.shift()) { handlePromise(result, task) } } return } if (isObject(result)) { let then = null try { then = result.then } catch (error) { reject(promise, error) } if (isFunction(then)) { try { let resolvePromise = function (y) { resolve(promise, y) } let rejectPromise = function (r) { reject(promise, r) } then.call(result, resolvePromise, rejectPromise) } catch (error) { reject(promise, error) } return } } promise._state = fulfilled promise._value = result if (promise._tasks && promise._tasks.length) { let task = null while (task = promise._tasks.shift()) { handlePromise(promise, task) } } }
reject將promise的狀態設置爲rejected, 並以當前的promise的狀態,執行promise中經過then註冊的onRejected回調。
function reject (promise, reason) { if (promise._state !== pending) { return } promise._state = rejected promise._value = reason let task while (task = promise._tasks.shift()) { handlePromise(promise, task) } }
handlePromise函數主要根據當前的promise的狀態, 以及內容(resolve或者reject的參數)。處理經過then註冊的回調。而且會鏈式的調用,註冊在then返回的新promise的上的then的回調
// 將回調的結果,傳入第二個then中 fn().then().then()
根據規範2.2.4, 以及規範給出的註解。當promise的狀態改變,onFulfilled, onRejected並不會當即執行,並且在本次的宏任務完成後,纔會執行onFulfilled或者onRejected。而setImmediate則是將代碼push到微任務隊列中。在宏任務中會清空微任務隊列。
function handlePromise (prevPromise, task) { // 須要在宏任務完後的微任務隊列中執行 setImmediate(() => { // nextPromise是then返回的promise const { onFulfilled, onRejected, promise: nextPromise } = task let callback = null let value = prevPromise._value let state = prevPromise._state if (state === fulfilled) { callback = onFulfilled } else if (state === rejected) { callback = onRejected } if (!callback) { // 若是在promise中沒有註冊callback if (state === fulfilled) { resolve(nextPromise, value) } else if (state === rejected) { reject(nextPromise, value) } } else { try { const result = callback(value) // 對then中返回promise處理 // 將callback返回的結果,帶入到新的promise中 resolve(nextPromise, result) } catch (error) { reject(nextPromise, error) } } }) }
Promise.resolve方法返回一個新的Promise對象,狀態爲resolved。Promise.reject(reason)方法也會返回一個新的 Promise實例,該實例的狀態爲rejected。
class Promise { // ... static resolve (result) { return new Promise((resolve) => { resolve(result) }) } static reject (reason) { return new Promise((_, reject) => { reject(reason) }) } }
Promise.all和Promise.race必須接受一個數組爲參數,數組中爲多個Promise的實例。Promise.all和Promise.race的使用我就再也不這裏贅述了。
Promise.all會使用計數器,記錄Promise數組中的全部Promise實例的狀態是否都變爲fulfilled態,若是計數器的長度和數組長度一致,咱們則會將Promise.all的狀態設置爲fulfilled態。
class Promise { static race (promises) { if (isArray(promises)) { let promisesLength = promises.length return new Promise((resolve, reject) => { for (let i = 0; i < promisesLength; i++) { promises[i].then((result) => { resolve(result) }).catch((error) => { reject(error) }) } }) } else { throw new TypeError('The arguments must be arrays') } } static all (promises) { if (isArray(promises)) { let promisesLength = promises.length let counter = 0 let resultList = [] return new Promise((resolve, reject) => { for (let i = 0; i < promisesLength; i++) { promises[i].then((result) => { counter += 1 resultList.push(result) if (counter === promisesLength) { resolve(resultList) } }).catch((error) => { reject(error) }) } }) } else { throw new TypeError('The arguments must be arrays') } } }