這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 1. 定義整體結構 先寫出構造函數,將Promise向外暴露 /* 自定義Promise函數模塊:IIFE */ (function (window) { /* Promise構造函數 executor:執行器函數 */ function ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
1. 定義整體結構
- 先寫出構造函數,將Promise向外暴露
/* 自定義Promise函數模塊:IIFE */ (function (window) { /* Promise構造函數 executor:執行器函數 */ function Promise(executor) { } // 向外暴露Promise window.Promise = Promise })()
- 添加Promise原型對象上的方法
/* Promise原型對象的then 指定一個成功/失敗的回調函數 返回一個新的promise對象 */ Promise.prototype.then = function(onResolved,onRejected){ } /* Promise原型對象的.catch 指定一個失敗的回調函數 返回一個新的promise對象 */ Promise.prototype.catch = function(onRejected){ }
- 添加Promise函數對象上的方法
/* Promise函數對象的resovle方法 返回一個指定結果的promise對象 */ Promise.resolve = function(value){ } /* Promise函數對象的reject方法 返回一個指定reason的失敗狀態的promise對象 */ Promise.reject = function(value){ } /* Promise函數對象的all方法 返回一個promise對象,只有當所有promise都成功時返回的promise狀態才成功 */ Promise.all = function(0value){ } /* Promise函數對象的race方法 返回一個promise對象,狀態由第一個完成的promise決定 */ Promise.race = function(value){ }
通過上面的註釋可以知道。不管是Promise原型對象上的方法還是Promise函數對象上的方法 ,它們的執行結果都將返回一個Promise對象
2. 實現Promise構造函數
我們看看我們是怎麼使用Promise的
const promiseA = new Promise( (resolve,reject) => { resolve(777); });
- 我們傳入了一個函數,而且這個函數被立即執行,不僅如此,這個函數還會立即執行resolve和reject。說明構造函數里有resolve和reject方法。因此我們可以初步實現:
/* Promise構造函數 executor:執行器函數 */ function Promise(executor) { function resovle() { } function reject() { } // 立即同步執行executor executor(resovle,reject) }
- 每個promise都有一個狀態可能為pending或resolved,rejected。而且初始狀態都為pending。因此需要添加個status來表示當前promise的狀態.。並且每個promise有自己的data。
function Promise(executor) { var self = self 新增代碼 self.status = 'pending' // 給promise對象指定status屬性,初始值為pending self.data = undefined // 給promise對象指定一個存儲結果的data function resovle() { } function reject() { } // 立即同步執行executor executor(resovle,reject) }
此外,當我們這樣使用Promise的時候,
// 例1 var promise = new Promise((resovle,reject)=>{ }) promise.then(resolve=>{},reject=>{})
這時執行到then,因為我們傳入的立即執行函數沒有執行resolve或者reject,所以promise的狀態還是pending,這時要把then裡面的回調函數保存起來,所以需要個callbacks數組
function Promise(executor) { var self = self self.status = 'pending' // 給promise對象指定status屬性,初始值為pending self.data = undefined // 給promise對象指定一個存儲結果的data 新增代碼 self.callbacks = [] // 每個元素的結構:{onResolved(){},onRejected(){}} function resovle() { } function reject() { } // 立即同步執行executor executor(resovle,reject) }
那 then函數是怎麼把傳入的回調收集起來的。其實很簡單,就是判斷當前promise是否為pending狀態,是的話,就把回調push到callbacks中。
Promise.prototype.then = function(onResolved,onRejected){ var self = this if(self.status === 'pending'){ // promise當前狀態還是pending狀態,將回調函數保存起來 self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved'){ }else{ } }
- 在上面的例子1的基礎上,當我們執行resovle(value)時,如例2
// 例2 var promise = new Promise((resolve,reject)=>{ setTimeout(function () { resolve(1) }) }) promise.then( value=>{console.log(value)}, err=>{console.log(err)} )
此時代碼執行情況是怎麼樣的呢?
- 先執行new Promise里的代碼,然後發現個定時器,js線程將定時器交給定時器線程處理,2. 然後繼續執行下麵的代碼,發現是then,而且當前的promise還是pending的狀態。就把then里的回調函數放到callbacks中。
- 5秒後定時器線程將定時器里的回調函數(也就是巨集任務)放到消息隊列中,js線程在消息隊列里發現了這個巨集任務,就把它拿來執行。
- 執行這個巨集任務,就執行了resolve(1),此時promise的callbacks里的回調被執行。並將當前promise狀態改為resolved。然後這個1也會被保存到當前promise對象中
那怎麼實現resolve呢?依舊上面的描述,就知道resovle的功能是執行callbacks里的函數,並保存data,並將當前promise狀態改為resolved。所以我們可以這麼實現
function resolve(value) { // 將狀態改為resolved self.status = 'resolved' // 保存value的值 self.data = value // 如果有待執行的callback函數,立即非同步執行回調函數onResolved if (self.callbacks.length>0){ self.callbacks.forEach(callbackObj=>{ callbackObj.onResolved(value) }) } }
- 我們還知道,promise的狀態只能改變一次,因此當執行resolve的時候要判斷是不是promise是不是pending的狀態,否則是不能執行的
function resolve(value) { // 如果當前狀態不是pending,則不執行 if(this.status !== 'pending'){ return } // 將狀態改為resolved this.status = 'resolved' // 保存value的值 this.data = value // 如果有待執行的callback函數,立即非同步執行回調函數onResolved if (this.callbacks.length>0){ setTimeout(()=>{ this.callbacks.forEach(callbackObj=>{ A callbackObj.onResolved(value) }) }) } }
- 異曲同工之妙的是reject方法也是這個道理,因此這裡無需贅述
function reject(value) { // 如果當前狀態不是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) }) } }
- 我們又知道,當在執行executor的時候,如果執行異常的話,這個promise的狀態會直接執行reject方法。例如
// 例 3 var promise = new Promise((resolve,reject)=>{ error;執行到這裡出錯了 setTimeout(function () { resolve(1) }) })
要實現這個功能,我們可以在executor外讓try catch來捕獲
try{ // 立即同步執行executor executor(resolve,reject) }catch (e) { // 如果執行器拋出異常,promise對象變為rejected狀態 reject(e) }
好了,現在測試下
// 例4 let promise = new Promise((resolve,reject)=>{ setTimeout(function () { resolve(1) //reject(1) },100) }) promise.then( value=>{ console.log("onResolved:",value); }, reason=>{ console.log("onRejected:",reason); } )
發現成功。 成功輸出onResolved:1
3. 實現then方法
我們在上面簡單的實現了當前promise為pending狀態的情況,如:
Promise.prototype.then = function(onResolved,onRejected){ var self = this if(self.status === 'pending'){ // promise當前狀態還是pending狀態,將回調函數保存起來 self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved'){ }else{ } }
那其他情況呢?
執行到then時,promise可能會是pending狀態,此時就要把then里的回調函數保存起來,也可能會是resolved或者rejected狀態,此時就不用把回調保存起來,直接執行onResolved或onRejected方法。註意是非同步執行。而且是做為微任務的,這裡我們簡單的用setTimeout來實現就好了。
Promise.prototype.then = function(onResolved,onRejected){ var self = this if(self.status === 'pending'){ // promise當前狀態還是pending狀態,將回調函數保存起來 self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved'){ setTimeout(()=>{ onResolved(self.data) }) }else{ setTimeout(()=>{ onResolved(self.data) }) } }
而且我們知道,執行完then是要返回一個新的promise的,而新的promise的狀態則由當前then的執行結果來確定。
Promise.prototype.then = function(onResolved,onRejected){ var self = this return new Promise((resolve,reject)=>{ if(self.status === 'pending'){ // promise當前狀態還是pending狀態,將回調函數保存起來 self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved'){ setTimeout(()=>{ onResolved(self.data) }) }else{ setTimeout(()=>{ onResolved(self.data) }) } }) }
當 當前的promise狀態為resolved的時候,則執行then的時候,會執行第二個判斷語句
則當前執行第二個判斷語句的時候會出現三種情況
- 如果then里的回調函數返回的不是promise,return的新promise的狀態是則是resolved,value就是返回的值。例如:
// 例5 let promise = new Promise((resolve,reject)=>{ resolve(1) }) promise.then( value=>{ return value //返回的不是promise,是value } )
因此,我們可以這樣實現
Promise.prototype.then = function(onResolved,onRejected){ var self = this return new Promise((resolve,reject)=>{ if(self.status === 'pending'){ // promise當前狀態還是pending狀態,將回調函數保存起來 self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved'){ 修改代碼 setTimeout(()=>{ const result = onResolved(self.data) if (result instanceof Promise){ } else { // 1. 如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。 resolve(result) } }) }else{ setTimeout(()=>{ onResolved(self.data) }) } }) }
簡單解釋下:
執行onResolved(self.data),其實就是執行例子中的下麵這個回調函數
value=>{ return value //返回的不是promise,是value }
那麼這個回調函數返回了value。就把value傳入resolve函數,resolve函數將當前新的promise的狀態改為resolved,同時將value保存到當前新的promise的data中。
- 如果回調函數返回的是promise,return的promise的結果就是這個promise的結果,如代碼所示,我們返回一個新的promise。如果這個promise執行了resolve,返回的新的promise的狀態則是resolved的。否則為rejected
// 例6 let promise = new Promise((resolve,reject)=>{ resolve(1) }) promise.then( value=>{ return new Promise((resolve,reject)=>{ resolve(2) //或者 //reject(error) }) } )
因此我們可以這樣實現
Promise.prototype.then = function(onResolved,onRejected){ var self = this return new Promise((resolve,reject)=>{ if(self.status === 'pending'){ // promise當前狀態還是pending狀態,將回調函數保存起來 self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} }) }else if(self.status === 'resolved'){ setTimeout(()=>{ const result = onResolved(self.data) if (result instanceof Promise){ // 2. 如果回調函數返回的是promise,return的promise的結果就是這個promise的結果 result.then( value => {resolve(value)}, reason => {reject(reason)} ) } else { // 1. 如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。 resolve(result) } }) }else{ setTimeout(()=>{ onResolved(self.data) }) } }) }
在這裡說明一下:
result.then( value => {resolve(value)}, reason => {reject(reason)} )
由於我們在例6中執行了then里的
value=>{ return new Promise((resolve,reject)=>{ resolve(2) //或者 //reject(error) }) }
則返回一個promise對象,這個promise對象可能為resolved狀態(執行 resolve(2))也可能為rejected狀態(執行reject(error))。
將會導致value => {resolve(value)},
這個回調函數的執行或者 reason => {reject(reason)}
的執行。
因此會把即將返回的新的promise的data設置為value或者,reason。會把狀態設置為resolved或者rejected。
- 如果執行這段代碼的時候拋出錯誤,則返回的promise的狀態為rejected,我們可以用try catch來實現
setTimeout(()=>{ try{ const result = onResolved(self.data) if (result instanceof Promise){ // 2. 如果回調函數返回的是promise,return的promise的結果就是這個promise的結果 result.then( value => {resolve(value)}, reason => {reject(reason)} ) } else { // 1. 如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。 resolve(result) } }catch (e) { // 3.如果執行onResolved的時候拋出錯誤,則返回的promise的狀態為rejected reject(e) } })
異曲同工之妙的是當status === 'rejected',道理一樣
setTimeout(()=>{ try{ const result = onRejected(self.data) if (result instanceof Promise){ // 2. 如果回調函數返回的是promise,return的promise的結果就是這個promise的結果 result.then( value => {resolve(value)}, reason => {reject(reason)} ) } else { // 1. 如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。 resolve(result) } }catch (e) { // 3.如果執行onResolved的時候拋出錯誤,則返回的promise的狀態為rejected reject(e) } })
到這裡,我們發現當執行resolve的時候,onResolved(self.data)
和onRejected(self.data)
執行時也會跟上面一樣的結果,可以說執行回調函數都要做以上判斷,因此我們要將
self.callbacks.push({ onResolved(){onResolved(self.data)}, onRejected(){onRejected(self.data)} })
改成
if(self.status === 'pending'){ // promise當前狀態還是pending狀態,將回調函數保存起來 self.callbacks.push({ onResolved(){ try{ const result = onResolved(self.data) if (result instanceof Promise){ // 2. 如果回調函數返回的是promise,return的promise的結果就是這個promise的結果 result.then( value => {resolve(value)}, reason => {reject(reason)} ) } else { // 1. 如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。 resolve(result) } }catch (e) { // 3.如果執行onResolved的時候拋出錯誤,則返回的promise的狀態為rejected reject(e) } },
到此,我們發現,相同的代碼太多了,因此有必要封裝一下
function handle(callback) { try{ const result = callback(self.data) if (result instanceof Promise){ // 2. 如果回調函數返回的是promise,return的promise的結果就是這個promise的結果 result.then( value => {resolve(value)}, reason => {reject(reason)} ) } else { // 1. 如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。 resolve(result) } }catch (e) { // 3.如果執行onResolved的時候拋出錯誤,則返回的promise的狀態為rejected reject(e) } }
這樣以來就清爽了很多
Promise.prototype.then = function(onResolved,onRejected){ var self = this return new Promise((resolve,reject)=>{ /* 調用指定回調函數的處理,根據執行結果。改變return的promise狀態 */ function handle(callback) { try{ const result = callback(self.data) if (result instanceof Promise){ // 2. 如果回調函數返回的是promise,return的promise的結果就是這個promise的結果 result.then( value => {resolve(value)}, reason => {reject(reason)} ) } else { // 1. 如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。 resolve(result) } }catch (e) { // 3.如果執行onResolved的時候拋出錯誤,則返回的promise的狀態為rejected reject(e) } } 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) }) } }) }
另外,我們還知道,promise會發生值傳透,例如
let promsie = new Promise((resolve,reject)=>{ resolve(1) }) promsie .then(2) .then(3) .then(value =>console.log(value))
運行結果: 1
解釋:.then 或者 .catch 的參數期望是函數,傳入非函數則會發生值穿透。值傳透可以理解為,當傳入then的不是函數的時候,這個then是無效的。而實際原理上其實是當then中傳入的不算函數,則這個then返回的promise的data,將會保存上一個的promise.data。這就是發生值穿透的原因。而且每一個無效的then所返回的promise的狀態都為resolved。
因此,要實現直傳透這個特性,我們可以這樣實現
添加這兩句來判斷要不要發生值傳透
onResolved = typeof onResolved === 'function'? onResolved: value => value onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason}
實際上就是改寫,如果傳入的不是函數,那就忽略那個傳入值,自己再寫一個函數。這個函數的執行結果將返回上一個promise的data
Promise.prototype.then = function(onResolved,onRejected){ onResolved = typeof onResolved === 'function'? onResolved: value => value onRejected = typeof onRejected === 'function'? onRejected: reason => {throw reason} var self = this return new Promise((resolve,reject)=>{ /* 調用指定回調函數的處理,根據執行結果。改變return的promise狀態 */ function handle(callback) { try{ const result = callback(self.data) if (result instanceof Promise){ // 2. 如果回調函數返回的是promise,return的promise的結果就是這個promise的結果 result.then( value => {resolve(value)}, reason => {reject(reason)} ) } else { // 1. 如果回調函數返回的不是promise,return的promise的狀態是resolved,value就是返回的值。 resolve(result) } }catch (e) { // 3.如果執行onResolved的時候拋出錯誤,則返回的promise的狀態為rejected reject(e) } } 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) }) } }) }
3.實現catch方法
catch方法的作用跟then里的第二歌回調函數一樣,因此我們可以這樣來實現
Promise.prototype.catch = function(onRejected){ return this.then(undefined,onRejected) }
我的天啊,居然這麼簡單
4. 實現Promise.resolve
我們都知道,Promise.resolve方法可以傳三種值
- 不是promise
- 成功狀態的promise
- 失敗狀態的promise
Promise.resolve(1) Promise.resolve(Promise.resolve(1)) Promise.resolve(Promise.reject(1))
實際上跟實現上面的then時有點像
Promise.resolve = function(value){ return new Promise((resolve,reject)=>{ if (value instanceof Promise){ // 如果value 是promise value.then( value => {resolve(value)}, reason => {reject(reason)} ) } else{ // 如果value不是promise resolve(value) } } }
5.實現Promise.reject
實現這個比較簡單,返回一個狀態為rejected的promise就好了
/* Promise函數對象的reject方法 返回一個指定reason的失敗狀態的promise對象 */ Promise.reject = function(reason){ return new Promise((resolve,reject)=>{ reject(reason) }) }
6.實現Promise.all
我們知道,這個方法會返回一個promise
/* Promise函數對象的all方法 返回一個promise對象,只有當所有promise都成功時返回的promise狀態才成功 */ Promise.all = function(promises){ return new Promise((resolve,reject)=>{ }) }
而這個promise的狀態由遍歷每個promise產生的結果決定
/* Promise函數對象的all方法 返回一個promise對象,只有當所有promise都成功時返回的promise狀態才成功 */ Promise.all = function(promises){ return new Promise((resolve,reject)=>{ // 遍歷promises,獲取每個promise的結果 promises.forEach((p,index)=>{ }) }) }
有兩種結果:
- 遍歷到有一個promise是reject狀態,則直接返回的promise狀態為rejected
Promise.all = function(promises){ return new Promise((resolve,reject)=>{ // 遍歷promises,獲取每個promise的結果 promises.forEach((p,index)=>{ p.then( value => { }, reason => { //只要有一個失敗,return的promise狀態就為reject reject(reason) } ) }) }) }
- 遍歷所有的promise的狀態都為resolved,則返回的promise狀態為resolved,並且還要每個promise產生的值傳遞下去
Promise.all = function(promises){ const values = new Array(promises.length) var resolvedCount = 0 //計狀態為resolved的promise的數量 return new Promise((resolve,reject)=>{ // 遍歷promises,獲取每個promise的結果 promises.forEach((p,index)=>{ p.then( value => { // p狀態為resolved,將值保存起來 values[index] = value resolvedCount++; // 如果全部p都為resolved狀態,return的promise狀態為resolved if(resolvedCount === promises.length){ resolve(values) } }, reason => { //只要有一個失敗,return的promise狀態就為reject reject(reason) } ) }) }) }
好像可以了,當其實這裡還有一個問題,就是all傳進去的數組不一定都是promise對象,可能是這樣的
all([p,2,3,p])
因此需要把不是promise的數字包裝成promise
Promise.all = function(promises){ const values = new Array(promises.length) var resolvedCount = 0 //計狀態為resolved的promise的數量 return new Promise((resolve,reject)=>{ // 遍歷promises,獲取每個promise的結果 promises.forEach((p,index)=>{ Promise.resolve(p).then( value => { // p狀態為resolved,將值保存起來 values[index] = value resolvedCount++; // 如果全部p都為resolved狀態,return的promise狀態為resolved if(resolvedCount === promises.length){ resolve(values) } }, reason => { //只要有一個失敗,return的promise狀態就為reject reject(reason) } ) }) }) }
7.實現Promise.race
這個方法的實現要比all簡單很多
/* Promise函數對象的race方法 返回一個promise對象,狀態由第一個完成的promise決定 */ Promise.race = function(promises){ return new Promise((resolve,reject)=>{ // 遍歷promises,獲取每個promise的結果 promises.forEach((p,index)=>{ Promise.resolve(p).then( value => { // 只要有一個成功,返回的promise的狀態九尾resolved resolve(value) }, reason => { //只要有一個失敗,return的promise狀態就為reject reject(reason) } ) }) }) }