This is my Promise

Promise專題

1.什么是Promise?

Promise是JS異步編程中的重要概念,異步抽象處理對象,是目前比較流行Javascript異步編程解決方案之一。

2.對于幾種常見異步編程方案?(暫不說generator函數(shù)和async函數(shù))

1.回調函數(shù);
2.事件監(jiān)聽
3.發(fā)布/訂閱模式
4.Promise對象

3.方案對比

這里為了突出Promise在異步編程的優(yōu)勢,我主要使用了回調函數(shù)Promise作出對比

1.回調函數(shù)

我們接觸比較多的回調函數(shù),我舉個例子,我們們用Jquery的ajax獲取數(shù)據(jù)時 都是以回調函數(shù)方式獲取的數(shù)據(jù)

$.get(url, (data) => {
    console.log(data)
)

如果說 當我們需要發(fā)送多個異步請求 并且每個請求之間需要相互依賴 那這時 我們只能 以嵌套方式來解決 形成 "回調地獄"

$.get(url, data1 => {
    console.log(data1)
    $.get(data1.url, data2 => {
        console.log(data1)
        $.get(data2.url, data3 => {
            console.log(data2)
              .....
        })
    })
})

這樣一來,在處理越多的異步邏輯時,就需要越深的回調嵌套,導致代碼書寫順序與執(zhí)行順序不一致,不利于閱讀與維護,不利于bug的修復和新需求的加入等等。

2.Promise方案

這里我們使用Promise來解決這個callback-hell問題。

const asyncRequest = url => {
    return new Promise((resolve,reject)=>{
        $.get(url, data => {
            resolve(data)
        });
    })
}
首先對Jq的請求函數(shù)進行Promise封裝


// 請求data1
asyncRequest(url)
.then(data1 => {
    return request(data1.url);   
})
.then(data2 => {
    return request(data2.url);
})
.then(data3 => {
    console.log(data3);
})
.catch(err => throw new Error(`error  of ${err} happen in asyncRequest function`));

經過Promise處理的Jq請求函數(shù),在處理上述中回調地獄情形是如此優(yōu)勢巨大,整個請求下來,邏輯非常清晰和有利于代碼的維護。


接下來為了更加深入地了解Promise,我們不如造一個屬于自己的Promise吧,不過這個Promise不會完全實現(xiàn)和PromiseA+規(guī)范的Promise,這里我們只需要實現(xiàn)我們平時使用的Promise中的靜態(tài)resolve,reject函數(shù),原型對象的then函數(shù),catch函數(shù),實現(xiàn)鏈式調用,而race和all函數(shù)這些附加性質的函數(shù),我們有時間再去實現(xiàn),我們先簡單實現(xiàn)上述目標!


等等,實現(xiàn)一個Promise之前,我們先了解Promise的特性,不然你不知道需求,寫什么代碼?。。?/p>

3.Promise特性

1.Promise 是一個構造函數(shù), new Promise 返回一個 promise對象 接收一個excutor執(zhí)行函數(shù)作為參數(shù), excutor有兩個函數(shù)類型形參resolve reject

const promise = new Promise((resolve, reject) => {
       // 異步處理
       // 處理結束后、調用resolve 或 reject
});

2.Promise是一個狀態(tài)機,有三種不同的狀態(tài)。

  • pending
  • fulfilled
  • rejected

(1) promise 對象初始化狀態(tài)為 pending
(2) 當調用resolve(成功),會由pending => fulfilled
(3) 當調用reject(失敗),會由pending => rejected

注意promsie狀態(tài) 只能由 pending => fulfilled/rejected, 一旦修改就不能再變

3.promise對象方法
(1) then方法注冊 當resolve(成功)/reject(失敗)的回調函數(shù)

// onFulfilled 是用來接收promise成功的值
// onRejected 是用來接收promise失敗的原因
promise.then(onFulfilled, onRejected);

(2) resolve(成功) onFulfilled會被調用

const promise = new Promise((resolve, reject) => {
   resolve('fulfilled'); // 狀態(tài)由 pending => fulfilled
});
promise.then(result => { // onFulfilled
    console.log(result); // 'fulfilled' 
}, reason => { // onRejected 不會被調用
})

(3) reject(失敗) onRejected會被調用

const promise = new Promise((resolve, reject) => {
   reject('rejected'); // 狀態(tài)由 pending => rejected
});
promise.then(result => { // onFulfilled 不會被調用
  
}, reason => { // onRejected 
    console.log(reason); // 'rejected'
})

(4) promise.catch
在鏈式寫法中可以捕獲前面then中發(fā)送的異常

promise.catch(onRejected)
相當于
promise.then(null, onRrejected);

// 注意
// onRejected 不能捕獲當前onFulfilled中的異常
promise.then(onFulfilled, onRrejected); 

// 可以寫成:
promise.then(onFulfilled)
       .catch(onRrejected); 

(5)鏈式調用
promise.then方法每次調用都返回一個新的promise對象所以可以鏈式寫法

function taskA() {
    console.log("Task A");
}
function taskB() {
    console.log("Task B");
}
function onRejected(error) {
    console.log("Catch Error: A or B", error);
}

var promise = Promise.resolve();
promise
    .then(taskA)
    .then(taskB)
    .catch(onRejected) // 捕獲前面then方法中的異常

(6)Promise的靜態(tài)方法

  • Promise.resolve 返回一個fulfilled狀態(tài)的promise對象
Promise.resolve('hello').then(function(value){
    console.log(value);
});

Promise.resolve('hello');
// 相當于
const promise = new Promise(resolve => {
   resolve('hello');
});

(2) Promise.reject 返回一個rejected狀態(tài)的promise對象

Promise.reject(24);
new Promise((resolve, reject) => {
   reject(24);
});

(3) Promise.all 接收一個promise對象數(shù)組為參數(shù)

只有全部為resolve才會調用 通常會用來處理 多個并行異步操作

const p1 = new Promise((resolve, reject) => {
   resolve(1);
});

const p2 = new Promise((resolve, reject) => {
   setTimeout(()=>{
       resolve(2);
    },1000)
   });

const p3 = new Promise((resolve, reject) => {
   reject(3);
});

Promise.all([p1, p2, p3]).then(data => { 
   console.log(data); // [1, 2, 3] 結果順序和promise實例數(shù)組順序是一致的
}, err => {
   console.log(err);
});

(4) Promise.race 接收一個promise對象數(shù)組為參數(shù)

Promise.race 只要有一個promise對象進入 FulFilled 或者 Rejected 狀態(tài)的話,就會繼續(xù)進行后面的處理。

function timerPromisefy(delay) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
Promise.race([
    timerPromisefy(10),
    timerPromisefy(20),
    timerPromisefy(30)
]).then(function (values) {
    console.log(values); // 10
});

Promise特征總結

1.Promise是一個構造函數(shù),接受一個函數(shù)作為參數(shù),而該函數(shù)兩個函數(shù)類型形參resolve reject,這兩函數(shù)是構造函數(shù)內部提供。
2.Promise有三種狀態(tài)pending,fulfilled,rejected且狀態(tài)不能逆轉
3.Promise對象有三個方法then,resolve,reject,catch
4.Promise能夠進行鏈式調用
5.Promise靜態(tài)方法resolve,reject,all,rece。


需求準備好,開始早一個Promise!(all,race暫不實現(xiàn),另外不對thenable對象,和內部可能存在的其他Promise的情形作處理,先通過數(shù)字字符串,對象等情況)

go on

首先我們要將下面這個回調函數(shù)做做處理變成Promise形式

function doSomething(cb) {
  cb();
}

biubiubiu

function doSomething(){
    return {
        then(cb) {
            cb()
        }
    }
}

好得到這段毫無意義的代碼,不過看上去好像有點樣式...!我們目前還沒有觸及 Promise 背后的核心概念,但這也是個小小的開始。

好繼續(xù)改造

上面代碼:
function MyPromise(){
    return {
        then(cb) {
            cb()
        }
    }
}

改造代碼:
function MyPromise(excutor){
    let callback = null;  
    this.then = function(cb) {
        callback = cb
    }
    function resolve(val) {      
        callback(val)
    }
    try{
        excutor(resolve)
    }catch(error){
       throw new Error(error)
    }

}

這里改造后的代碼,好像有模有樣了,但是這里就遇到一個問題:如果你逐行執(zhí)行代碼,就會發(fā)現(xiàn)resolve() 函數(shù)then() 函數(shù)之前被調用,這就意味著resolve() 被調用的時候,callback 還是 null。讓我們用一個 hack 來干掉這個問題,引入setTimeout處理一下,代碼如下所示:

function MyPromise(excutor){
    let callback = null;  
    this.then = function(cb) {
        callback = cb
    }
    function resolve(val) {      
        setTimeout(()=>{
           callback(val)
        },0)
    }
    try{
        excutor(resolve)
    }catch(error){
       throw new Error(error)
    }
}

測試代碼:
console.log(0)
var a = new MyPromise((resolve,reject)=>{
    console.log(1);
    resolve(2);
})
a.then(val=>{
    console.log(val)
})
console.log(3)
結果:0,1,3,2

做到這里,程序的執(zhí)行順序和我們平常的Promise順序一致唉!不過還存在非常多的問題!

  • 沒有鏈式調用
  • 沒有Promise狀態(tài)
  • 沒有reject函數(shù)作錯誤返回,catch函數(shù)做錯誤捕捉等等...

好繼續(xù)改造

// promise 三個狀態(tài)
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

//這里用用es6的語法
class MyPromise {
  constructor(excutor) {
      if(typeof excutor !== 'function') {
          throw new Error(`${excutor} is not a function`);
      }
      let that = this;
      this.state = PENDING//fulfilled reject
      this.value = undefined;
      this.reason = undefined;
      this.onFulfilledCallbacks = [];//紀錄成功的回調,就是then的第一個參數(shù)
      this.onRejectedCallbacks = [];//紀錄成功的回調,就是then的第二個參數(shù)

    function resolve(value) {
       setTimeout(()=>{
           that.state = FULFILLED;
           that.value =value;
           that.onFulfilledCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.value);
           })
        })
    }
    function reject(reason) {
         setTimeout(()=>{
            that.state  = REJECTED;
            that.reason = reason;
            that.onRejectedCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.reason);
              })
           },0)
     }

      try {
         excutor(resolve,reject);
      } catch (error) {
          reject(error);
      }
  }

  then(onFulfilled,onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason=>{
            throw new Error(reason)
        }
       return new MyPromise((resolve,reject)=>{
/*
*這里是箭頭函數(shù),所以這里this.onFulfilledCallbacks和*this.onRejectedCallbacks是指向外層的成功和失敗回調數(shù)據(jù),
*假如是function的形式,就需要提前定義這個**參數(shù)函數(shù)**然后通過*bind()函數(shù)綁定this的指向
*/            
        this.onFulfilledCallbacks.push(value)=>{
                try {
                    let newValue = onFulfilled(value);
                    resolve(newValue);
                 } catch (error) {
                    reject(error)
                }               
             }
              this.onRejectedCallbacks.push(value=>{
                  try {
                      let reason = onRejected(value);
                      reject(reason)
                  } catch (error) {
                      reject(error)
                  }
            })
       })
  }

}

測試代碼:
console.log(0)
 var a = new SuperPromise((resolve,reject)=>{
     console.log(1)
     resolve(2)
 })

 a.then((val)=>{
     console.log(val)
     return (3)
 }).then(val=>{
     console.log(val)
 })
 console.log(4)
結果//0,1,4,2,3

好,這里我們實現(xiàn)了then的鏈式調用,這一看,我們只實現(xiàn)了原型上一個then函數(shù),接下來我們繼續(xù)實現(xiàn)catch,和靜態(tài)resolve,靜態(tài)reject

go on

上面我們提到catch函數(shù):
promise.catch(onRejected)
相當于
promise.then(null, onRrejected);
但是有一點不同的是,onRrejected不是不能捕捉到onFulfilled中的異常。

class MyPromise {
  constructor(excutor) {
      if(typeof excutor !== 'function') {
          throw new Error(`${excutor} is not a function`);
      }
      let that = this;
      this.state = PENDING//fulfilled reject
      this.value = undefined;
      this.reason = undefined;
      this.onFulfilledCallbacks = [];//紀錄成功的回調,就是then的第一個參數(shù)
      this.onRejectedCallbacks = [];//紀錄成功的回調,就是then的第二個參數(shù)

    function resolve(value) {
       setTimeout(()=>{
           that.state = FULFILLED;
           that.value =value;
           that.onFulfilledCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.value);
           })
        })
    }
    function reject(reason) {
         setTimeout(()=>{
            that.state  = REJECTED;
            that.reason = reason;
            that.onRejectedCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.reason);
              })
           },0)
     }

      try {
         excutor(resolve,reject);
      } catch (error) {
          reject(error);
      }
  }

     then(onFulfilled,onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason=>{
            throw new Error(reason)
        }        
       if(this.state === PENDING) {
            return new MyPromise((resolve,reject)=>{
                this.onFulfilledCallbacks.push(value=>{
                    try {
                        let newValue = onFulfilled(value);
                        resolve(newValue);
                    } catch (error) {
                        reject(error)
                    }
                })

                this.onRejectedCallbacks.push(value=>{
                    try {
                        let newValue = onRejected(value);
                        reject(newValue)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
        }
     }

  catch(onRejected) {
      return this.then(null,onRejected)
  }
}

測試代碼:
 var b = new MyPromise((resolve,reject)=>{
     reject('error happen in SuperPromise')
 })

 b.then((val)=>{
     console.log(val,'val')
     return val;
 })
 .catch(err=>{
     console.log(err)
 })


-------
 var c = new MyPromise((resolve,reject)=>{
     resolve(2)
 })

 c.then((val)=>{
     console.log(val,'val')
     throw new Error('some error in then')
 })
 .catch(err=>{
     console.log(err)
 })

接下黎啊繼續(xù)實現(xiàn)靜態(tài)resolve和reject

class MyPromise {
  constructor(excutor) {
      if(typeof excutor !== 'function') {
          throw new Error(`${excutor} is not a function`);
      }
      let that = this;
      this.state = PENDING//fulfilled reject
      this.value = undefined;
      this.reason = undefined;
      this.onFulfilledCallbacks = [];//紀錄成功的回調,就是then的第一個參數(shù)
      this.onRejectedCallbacks = [];//紀錄成功的回調,就是then的第二個參數(shù)

    function resolve(value) {
       setTimeout(()=>{
           that.state = FULFILLED;
           that.value =value;
           that.onFulfilledCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.value);
           })
        })
    }
    function reject(reason) {
         setTimeout(()=>{
            that.state  = REJECTED;
            that.reason = reason;
            that.onRejectedCallbacks.forEach((cb,idx)=>{
                 cb.call(this,that.reason);
              })
           },0)
     }

      try {
         excutor(resolve,reject);
      } catch (error) {
          reject(error);
      }
  }

     then(onFulfilled,onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason=>{
            throw new Error(reason)
        }        
       if(this.state === PENDING) {
            return new MyPromise((resolve,reject)=>{
                this.onFulfilledCallbacks.push(value=>{
                    try {
                        let newValue = onFulfilled(value);
                        resolve(newValue);
                    } catch (error) {
                        reject(error)
                    }
                })

                this.onRejectedCallbacks.push(value=>{
                    try {
                        let newValue = onRejected(value);
                        reject(newValue)
                    } catch (error) {
                        reject(error)
                    }
                })
            })
        }
     }

  catch(onRejected) {
      return this.then(null,onRejected)
  }

//這兩個就比較簡單,我們回憶一下,當我們Promise.resolve(),就會馬上進入成功回調,相反Promise.reject(),就馬上進入失敗回調

  static resolve(val){
      return new MyPromise((resolve,reject)=>{
            resolve(val)
      })
  }

  static reject(reason){
      return new MyPromise((resolve,reject)=>{
          reject(reason)
      })
   }
}

測試代碼:
 MyPromise.resolve(2).then(val=>{
     console.log(val)
     return 3
 }).then(val=>{
     console.log(val)
 })

---
 MyPromise.reject('some error happen in static reject')
 .catch(err => console.log(err))

以上兩段代碼測試通過,完美!!!
好,我們再看看我們的需求

(all,race暫不實現(xiàn),另外不對thenable對象,和內部可能存在的其他Promise的情形作處理,先通過數(shù)字,字符串,對象等情況)

OK這里已經實現(xiàn)的Promise已經實現(xiàn)了我們上述的請求,如果看客跟著我一起去實現(xiàn)一個Promise,是不是會有一些成就感呢?說笑,這節(jié)說到這里。good night

good night
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容