Promise核心實現
核心
構造函數核心
- 維護狀態變數,只能由pending變為resolve或者reject
- 維護一個存儲結果的變數
- 維護一個回調數組,執行到then,如果我們傳入的立即執行函數沒有立即執行resolve或者reject,所以promise的狀態還是pending,這時要把then裡面的回調函數保存起來。待到resolve或者reject執行後則執行回調數組裡存到方法。若傳入的立即執行函數直接執行了resolve或者reject此時就不用把回調保存起來,直接執行onResolved或onRejected方法。注意是非同步執行。而且是做為微任務的。(下面我們用setTimeout模擬)
then核心
- 執行時,若當前狀態為pending,則把回調保存到回調數組,若為resolve或者reject則直接非同步執行(微任務)。如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。即resolve(result)或者reject(result)
- 每次then都返回一個新的Promise。
- promise會發生值傳透,當then中傳入的不算函數,則這個then返回的promise的data,將會保存上一個的promise.data。這就是發生值穿透的原因。而且每一個無效的then所返回的promise的狀態都為resolved。
核心實現
將Promise向外暴露
(function (window) { /* Promise構造函數 executor:執行器函數 */ function Promise(executor) { } // 向外暴露Promise window.Promise = Promise })()
構造函數實現
function MyPromise(exector) { var self = this; self.status = 'pending'; // 給promise對象指定status屬性,初始值為pending self.data = undefined; // 給promise對象指定一個存儲結果的data self.callbacks = []; // 每個元素的結構 {onResolved(){},onRejected(){}} function resolve(value) { if(self.status !== 'pending') { return } self.status = 'resolved'; // 將狀態更改為resolved self.data = value; // 保存value的值 // 非同步調用resolve才會在這裡執行 // 如果有待執行的callback函數,立即非同步執行回調函數onResolved(); if(self.callbacks.length > 0) { self.callbacks.forEach(callbacksObj => { callbacksObj.onResolved(value); }); } } function reject() { // 如果當前狀態不是pending,則不執行 if(self.status !== 'pending'){ return } // 將狀態改為rejected self.status = 'rejected'; // 保存value的值 self.data = value; // 如果有待執行的callback函數,立即非同步執行回調函數onResolved if (self.callbacks.length>0){ self.callbacks.forEach(callbackObj=>{ callbackObj.onRejected(value) }) } } // 立即同步執行exector // 注意⚠️:當在執行executor的時候,如果執行異常的話,這個promise的狀態會直接執行reject方法 try{ // 立即同步執行executor executor(resolve,reject); } catch (e) { // 如果執行器拋出異常,promise對象變為rejected狀態 reject(e); } }
then實現
MyPromise.prototype.then = function(onResolved, onReject) { var self = this; // 處理值穿透 onResolved = typeof onResolved === 'function'? onResolved: value => value onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason} return new myPromise((resolve,reject) => { if(self.status === 'pending') { // promise當前狀態還是pending狀態,將回調函數保存起來 self.callbacks.push({ onResolved() { handle(onResolved) }, onRejected() { handle(onRejected) } }) } else if (self.status === 'resolved') { setTimeout(()=>{ handle(onResolved) }) } else { // 當status === 'rejected' setTimeout(()=>{ handle(onRejected) }) } // 處理函數 function handle(callback) { try { const result = callback(self.data) if (result instanceof MyPromise){ // 如果回調函數返回的是promise,return的promise的結果就是這個promise的結果 result.then( value => {resolve(value)}, reason => {reject(reason)} ) } else { // 如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。 resolve(result) } } catch (e) { // 如果執行onResolved的時候拋出錯誤,則返回的promise的狀態為rejected reject(e) } } }); }
其他方法實現
catch
借用then方法
MyPromise.prototype.catch = function(onRejected){ return this.then(undefined,onRejected) }
finally
借用then方法
MyPromise.prototype.finally = (onFinally) => { return this.then((res)=>{ MyPromise.resolve(onFinally()).then(()=> res) },(reson)=>{ MyPromise.resolve(onFinally()).then(()=> reson) }) }
resolve
Promise參數可以為如下三種
- 不是promise
- 成功狀態的promise
- 失敗狀態的promise
MyPromise.resolve = function(value){ return new MyPromise((resolve,reject)=>{ if (value instanceof Promise){ // 如果value 是promise value.then( value => {resolve(value)}, reason => {reject(reason)} ) } else{ // 如果value不是promise resolve(value) } } }
reject
MyPromise.reject = function(reason) { return new MyPromise((resolve,reject)=>{ reject(reason) }) }
all
- 入參一般是個由Promise實例組成的數組,但是也可以不是數組,但必須具有 Iterator 介面,且返回的每個成員都是 Promise 實例。若參數如果不是 Promise 實例,就會先調用Promise.resolve()方法,將參數轉為 Promise 實例,再進一步處理。
- 返回值是個promise,因為可以使用.then
- 如果全部成功,狀態變為resolved, 並且返回值組成一個數組傳給回調
- 但凡有一個失敗,狀態變為rejected, 並將error返回給回調
MyPromise.all = (promisesArr) => { // 返回Promise return new MyPromise((resolve, reject) => { let dataArr = new Array(promisesArr.length); let count = 0; for (let i = 0; i < promisesArr.length; i++) { // 在 .then 中收集數據,並添加 .catch,在某一個 Promise 遇到錯誤隨時 reject。 // 這樣,在最外面調用 Promise.all().catch() 時也可以 catch 錯誤資訊 // 判斷當前這個元素是否為Promise對象,不是則轉為Promise對象 let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]); currentPromise.then(res => { dataArr[index] = data; count++; // 如果數據收集完了,就把收集的數據 resolve 出去 if (count === promisesArr.length) resolve(dataArr); }).catch(err => { //如果某一個失敗,promise.all()立即執行reject回調。 //但剩餘的promise依舊繼續執行,只不過對promise.all的結果不會產生影響了 reject(err) }); } })
注意⚠️:dataArr添加時用下標而不用數組時為了防止順序錯亂
race
返回一個promise對象,狀態由第一個完成的promise決定
MyPromise.race = function(promisesArr){ return new Promise((resolve,reject)=>{ // 遍歷promises,獲取每個promise的結果 for (let i = 0; i < promisesArr.length; i++) { // 判斷當前這個元素是否為Promise對象,不是則轉為Promise對象 let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]); currentPromise.then( value => { // 只要有一個成功,返回的promise的狀態九尾resolved resolve(value) }, reason => { //只要有一個失敗,return的promise狀態就為reject reject(reason) } ) }) }) }
allSettled
Promise.allSettled() 方法返回一個在所有給定的 promise 已被決議或被拒絕後決議的 promise,並帶有一個對象數組,每個對象表示對應的promise 結果。
Promise.newAllSettled = function (promisesArr) { return new Promise((resolve, reject) => { let results = []; let count = 0; let promisesArrLength = promisesArr.length; // 運行所有的 Promise for (let i = 0; i < promisesArr.length; i++) { // 判斷當前這個元素是否為Promise對象,不是則轉為Promise對象 let currentPromise = (promisesArr[i] instanceof Promise) ? promisesArr[i] : Promise.resolve(promisesArr[i]); currentPromise.then(res => { // 當有 Promise 被 resolve 之後,記錄 resolve 值和狀態,已決 Promise 計數加一 results.push({value: res, status: 'fulfilled'}); count++; // 全部 Promise 已決,resolve if (count === promisesArrLength) { resolve(results); } }).catch(err => { // 當有 Promise 被 reject 後,記錄 reject 值和狀態,並且已決的 Promise 計數加一 results.push({value: err, status: 'rejected'}); count++; if (count === promisesArrLength) { resolve(results); } }); } }) };
參考
//juejin.im/post/6844904088963022856
//juejin.im/post/6850037281206566919
//blog.csdn.net/MichelleZhai/article/details/104475521
//zhuanlan.zhihu.com/p/60287801
//zhuanlan.zhihu.com/p/41502945
//zhuanlan.zhihu.com/p/61681036