前言
通過自定義編寫promise,可以更加深刻理解Promise的用法,以及學會對別人封裝的代碼做定制化使用。
自定義Promise
/** * 自定義Promise函數模塊,IIFE */ (function(window) { const PENDING = 'pending'; const RESOLVED = 'resolved'; const REJECTED = 'rejected'; /** * Promise構造函數 * executor: 執行器函數(同步執行) */ function Promise(executor) { const self = this; self.status = PENDING; //給promise對象指定status屬性,初始值為pending self.data = undefined; //給promise對象指定一個用於存儲結果數據的屬性 self.callbacks = []; //每個元素的數據結構: {onResolved(){}, onReject(){}} function resolve(value) { //如果當前狀態不是pending,直接結束 if(self.status !== PENDING) { return; } //狀態為resolved self.status = RESOLVED; //保存value數據 self.data = value; //如果有待執行的callback函數,立即異步執行回調函數onResolved if(self.callbacks.length>0) { setTimeout(() => { self.callbacks.forEach(callbacksObj => { callbacksObj.onResolved(value); }) }) } } function reject(reason) { //如果當前狀態不是pending,直接結束 if(self.status !== PENDING) { return; } //狀態為rejected self.status = REJECTED; //保存value數據 self.data = reason; //如果有待執行的callback函數,立即異步執行回調函數onResolved if(self.callbacks.length>0) { setTimeout(() => { self.callbacks.forEach(callbacksObj => { callbacksObj.onRejected(reason); }) }) } } try { executor(resolve, reject); } catch (error) { // 如果執行器拋出異常,primose對象變成rejected狀態 reject(error); } } /** * Promise原型對象的then() * 指定成功和失敗的回調函數 * 返回一個新的promise對象 */ Promise.prototype.then = function(onResolved, onRejected) { //向后傳遞成功的value onResolved = typeof onResolved === 'function' ? onResolved : value => value //指定默認的失敗的回調(實現錯誤/異常穿透的關鍵點),向后傳遞失敗的reason onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason} const self = this; //返回一個新的promise對象 return new Promise((resolve, reject) => { //調用指定的回調函數處理,根據執行的結果改變return的promise狀態 function handle(callback) { /** * 1.如果拋出異常,return的promise就會失敗,reason就是error * 2.如果回調函數返回不是promise,return的promise就會成功,value就是返回的值 * 3.如果回調函數返回是promise,return的promise就是這個promise結果 */ try { const result = callback(self.data); if(result instanceof Promise) { // result.then( // value => resolve(value), //當result成功時,讓return的promise也成功 // reason => reject(reason) //當result失敗時,讓return的promise也失敗 // ) result.then(resolve, reject); } else { resolve(result); } } catch(error) { reject(error); } } if(self.status === PENDING) { //假設當前還是pending狀態,將回調函數保存起來 self.callbacks.push({ onResolved() { handle(onResolved) }, onRejected() { handle(onRejected) } }) } else if(self.status === RESOLVED) { //如果當前是resolved狀態,異步執行onResolved函數並改變return的promise狀態 setTimeout(() => { handle(onResolved); }) } else { //如果當前是rejected狀態,異步執行onRejected函數並改變return的promise狀態 setTimeout(() => { handle(onRejected); }) } }); } /** * Promise原型對象的catch() * 指定失敗的回調函數 * 返回一個新的promise函數 */ Promise.prototype.catch = function(onRejected) { return this.then(undefined, onRejected); } // 函數對象方法,也即工具方法 /** * Promise函數對象的resolve方法 * 返回一個指定成功結果value的promise */ Promise.resolve = function(value) { //返回一個成功/失敗的promise return new Promise((resolve, reject) => { //value是promise if(value instanceof Promise) { value.then(resolve, reject); } else { //value不是promise resolve(value); } }) } /** * Promise函數對象的reject方法 * 返回一個指定失敗結果reason的promise */ Promise.reject = function(reason) { //返回一個失敗的promise return new Promise((resolve, reject) => { reject(reason); }) } /** * Promise函數對象的all方法 */ Promise.all = function(promises) { // 保存所有成功的value數組 const values = new Array(promises.length); // 用來保存成功的promise的數量 let resolvedCount = 0; return new Promise((resolve, reject) => { //遍歷獲取每一個primise的結果 promises.forEach((p, index) => { Promise.resolve(p).then( value => { resolvedCount++; //成功的數量加1 values[index] = value; //p成功了,將成功的value按照順序保存到values中 //如果全部成功了,將return的promise改為成功 if(resolvedCount === promises.length) { resolve(values); } }, reason => { reject(reason); } ) }) }) } /** * Promise函數對象的race方法 * 返回新的promise,其結果由第一個完成的promise決定 */ Promise.race = function(promises) { //返回一個promise return new Promise((resolve, reject) => { //遍歷獲取每一個primise的結果 promises.forEach((p, index) => { Promise.resolve(p).then( //Promise.resolve(p) 包裝了下p為promise,兼容數組中非promise的值 value => { //一旦有成功,將return變成成功 resolve(value); }, reason => { //一旦有失敗,將return變成失敗 reject(reason); } ) }) }) } /** * 返回一個promise對象,它在指定的時間后才確定結果 */ Promise.resolveDelay = function(value, time) { //返回一個成功/失敗的promise return new Promise((resolve, reject) => { setTimeout(() => { //value是promise if(value instanceof Promise) { value.then(resolve, reject); } else { //value不是promise resolve(value); } }, time) }) } /** * 返回一個promise對象,它在指定的時間后才失敗 */ Promise.rejectDelay = function(reason, time) { //返回一個失敗的promise return new Promise((resolve, reject) => { setTimeout(() => { reject(reason); }, time) }) } // 向外暴露Promise函數 window.Promise = Promise })(window)
應用舉例
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./promise.js"></script> <script type="text/javascript"> // console.log(new Promise().status) // const p = new Promise((resolve, reject) => { // setTimeout(() =>{ // // resolve(1); // reject(2); // console.log('-----') // }, 100) // }) // p.then( // value => { // console.log('onResolve1()', value); // }, // reason => { // console.log('onRejected1()', reason); // } // ) // p.then( // value => { // console.log('onResolve2()', value); // }, // reason => { // console.log('onRejected2()', reason); // } // ) const p = new Promise((resolve, reject) => { setTimeout(() =>{ resolve(1); // reject(2); console.log('-----') }, 100) }).then( value => { console.log('onResolve1()', value); return new Promise((resolve, reject) => reject(5)) }, reason => { console.log('onRejected1()', reason); return 4; } ).then( value => { console.log('onResolve2()', value); }, reason => { console.log('onRejected2()', reason); throw 6; } ).catch( reason => { console.log('onRejected catch()', reason); return new Promise(()=>{}) } ).then( value => { console.log('onResolve3()', value); }, reason => { console.log('onRejected3()', reason); throw 6; } ) </script> </head> <body> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./promise.js"></script> <script type="text/javascript"> const p1 = Promise.resolve(2); const p2 = Promise.resolve(Promise.resolve(3)); const p3 = Promise.resolve(Promise.reject(4)); p1.then(value => {console.log('p1', value)}) p2.then(value => {console.log('p2', value)}) p3.catch(reason => {console.log('p3', reason)}) </script> </head> <body> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./promise.js"></script> <script type="text/javascript"> const p1 = Promise.resolve(2); const p2 = Promise.resolve(Promise.resolve(3)); const p3 = Promise.resolve(Promise.reject(4)); const p4 = new Promise((resolve) => { setTimeout(() => { resolve(5); }, 1000) }) const pAll = Promise.all([p4, p1, p2]); pAll.then( values => { console.log('all onResolved()', values); }, reasons => { console.log('all onRejected()', reasons); } ) // const pRace = Promise.race([p1, p2, p3]); // pRace.then( // value => { // console.log('race onResolved()', value); // }, // reason => { // console.log('race onRejected()', reason); // } // ) </script> </head> <body> </body> </html>
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> <script src="./promise.js"></script> <script type="text/javascript"> const p1 = Promise.resolve(2); const p2 = Promise.resolve(Promise.resolve(3)); const p3 = Promise.resolve(Promise.reject(4)); const p4 = new Promise((resolve) => { setTimeout(() => { resolve(5); }, 1000) }) const p5 = Promise.reject(6) const pRace = Promise.race([p4, p5, p3]); pRace.then( value => { console.log('race onResolved()', value); }, reason => { console.log('race onRejected()', reason); } ) const p6 = Promise.resolveDelay(66, 3000); const p7 = Promise.rejectDelay(77, 2000); p6.then( value => { console.log(value); } ) p7.catch( reason => { console.log(reason); } ) </script> </head> <body> </body> </html>
當然,還能改造為class對象
/** * 自定義Promise函數模塊,IIFE */ (function(window) { const PENDING = 'pending'; const RESOLVED = 'resolved'; const REJECTED = 'rejected'; class Promise() { /** * Promise構造函數 * executor: 執行器函數(同步執行) */ constructor(executor) { const self = this; self.status = PENDING; //給promise對象指定status屬性,初始值為pending self.data = undefined; //給promise對象指定一個用於存儲結果數據的屬性 self.callbacks = []; //每個元素的數據結構: {onResolved(){}, onReject(){}} function resolve(value) { //如果當前狀態不是pending,直接結束 if(self.status !== PENDING) { return; } //狀態為resolved self.status = RESOLVED; //保存value數據 self.data = value; //如果有待執行的callback函數,立即異步執行回調函數onResolved if(self.callbacks.length>0) { setTimeout(() => { self.callbacks.forEach(callbacksObj => { callbacksObj.onResolved(value); }) }) } } function reject(reason) { //如果當前狀態不是pending,直接結束 if(self.status !== PENDING) { return; } //狀態為rejected self.status = REJECTED; //保存value數據 self.data = reason; //如果有待執行的callback函數,立即異步執行回調函數onResolved if(self.callbacks.length>0) { setTimeout(() => { self.callbacks.forEach(callbacksObj => { callbacksObj.onRejected(reason); }) }) } } try { executor(resolve, reject); } catch (error) { // 如果執行器拋出異常,primose對象變成rejected狀態 reject(error); } } /** * Promise原型對象的then() * 指定成功和失敗的回調函數 * 返回一個新的promise對象 */ then(onResolved, onRejected) { //向后傳遞成功的value onResolved = typeof onResolved === 'function' ? onResolved : value => value //指定默認的失敗的回調(實現錯誤/異常穿透的關鍵點),向后傳遞失敗的reason onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason} const self = this; //返回一個新的promise對象 return new Promise((resolve, reject) => { //調用指定的回調函數處理,根據執行的結果改變return的promise狀態 function handle(callback) { /** * 1.如果拋出異常,return的promise就會失敗,reason就是error * 2.如果回調函數返回不是promise,return的promise就會成功,value就是返回的值 * 3.如果回調函數返回是promise,return的promise就是這個promise結果 */ try { const result = callback(self.data); if(result instanceof Promise) { // result.then( // value => resolve(value), //當result成功時,讓return的promise也成功 // reason => reject(reason) //當result失敗時,讓return的promise也失敗 // ) result.then(resolve, reject); } else { resolve(result); } } catch(error) { reject(error); } } if(self.status === PENDING) { //假設當前還是pending狀態,將回調函數保存起來 self.callbacks.push({ onResolved() { handle(onResolved) }, onRejected() { handle(onRejected) } }) } else if(self.status === RESOLVED) { //如果當前是resolved狀態,異步執行onResolved函數並改變return的promise狀態 setTimeout(() => { handle(onResolved); }) } else { //如果當前是rejected狀態,異步執行onRejected函數並改變return的promise狀態 setTimeout(() => { handle(onRejected); }) } }); } /** * Promise原型對象的catch() * 指定失敗的回調函數 * 返回一個新的promise函數 */ catch(onRejected) { return this.then(undefined, onRejected); } // 函數對象方法,也即工具方法 /** * Promise函數對象的resolve方法 * 返回一個指定成功結果value的promise */ static resolve = function(value) { //返回一個成功/失敗的promise return new Promise((resolve, reject) => { //value是promise if(value instanceof Promise) { value.then(resolve, reject); } else { //value不是promise resolve(value); } }) } /** * Promise函數對象的reject方法 * 返回一個指定失敗結果reason的promise */ static reject = function(reason) { //返回一個失敗的promise return new Promise((resolve, reject) => { reject(reason); }) } /** * Promise函數對象的all方法 */ static all = function(promises) { // 保存所有成功的value數組 const values = new Array(promises.length); // 用來保存成功的promise的數量 let resolvedCount = 0; return new Promise((resolve, reject) => { //遍歷獲取每一個primise的結果 promises.forEach((p, index) => { Promise.resolve(p).then( value => { resolvedCount++; //成功的數量加1 values[index] = value; //p成功了,將成功的value按照順序保存到values中 //如果全部成功了,將return的promise改為成功 if(resolvedCount === promises.length) { resolve(values); } }, reason => { reject(reason); } ) }) }) } /** * Promise函數對象的race方法 * 返回新的promise,其結果由第一個完成的promise決定 */ static race = function(promises) { //返回一個promise return new Promise((resolve, reject) => { //遍歷獲取每一個primise的結果 promises.forEach((p, index) => { Promise.resolve(p).then( //Promise.resolve(p) 包裝了下p為promise,兼容數組中非promise的值 value => { //一旦有成功,將return變成成功 resolve(value); }, reason => { //一旦有失敗,將return變成失敗 reject(reason); } ) }) }) } /** * 返回一個promise對象,它在指定的時間后才確定結果 */ static resolveDelay = function(value, time) { //返回一個成功/失敗的promise return new Promise((resolve, reject) => { setTimeout(() => { //value是promise if(value instanceof Promise) { value.then(resolve, reject); } else { //value不是promise resolve(value); } }, time) }) } /** * 返回一個promise對象,它在指定的時間后才失敗 */ static rejectDelay = function(reason, time) { //返回一個失敗的promise return new Promise((resolve, reject) => { setTimeout(() => { reject(reason); }, time) }) } } // 向外暴露Promise函數 window.Promise = Promise })(window)
注意點:
1、函數對象和實例對象的區別
2、同步調用和異步調用
3、Promise的幾個API