最最最通俗易懂的promise手寫系列(二)- 鏈式調用

前言

上一次咱們寫完了Promise的基礎功能,明白了promise爲什麼能在任務成功的時候,調成功的回調函數,爲什麼在任務失敗的時候調失敗的回調函數.今天就把最麻煩的promise鏈式調用給搞定吧.javascript

附上上篇文章的連接:最最最通俗易懂的promise手寫系列(一)
java

再附上上次的代碼吧,以避免翻來翻去麻煩.promise

function Promise(executor) {
    let self = this;
    self.value = undefined;
    self.reason = undefined;
    self.status = 'pending';

    self.onFulFilledCallbacks = [];
    self.onRejectedCallbacks = [];

    function resolve(value) {
        if (self.status === 'pending') {
            self.value = value;
            self.status = 'resolved'

            self.onFulFilledCallbacks.forEach(onFulFilled => {
                onFulFilled(self.value)
            });
        }
    }
    function reject(reason) {
        if (self.status === 'pending') {
            self.reason = reason;
            self.status = 'rejected';
            self.onRejectedCallbacks.forEach(onRejected => {
                onRejected(self.reason)
            });
        }
    }

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

Promise.prototype.then = function (onFulFilled, onRejected) {
    if (this.status === 'pending') {
        this.onFulFilledCallbacks.push(() => {
            onFulFilled(this.value)
        }); 
        this.onRejectedCallbacks.push(() => {
            onRejected(this.reason)
        })
    } else if (this.status === 'resolved') {
        onFulFilled(this.value);
    } else if (this.status === 'rejected') {
        onRejected(this.reason);
    }
}
複製代碼

咱們要完成的目標:異步

let p = new Promise(function (resove, reject) {
  setTimeout(() => {
    console.log('任務執行完了');
    resove()
  },1500)
});

p.then(function (value) {
      console.log('第一個成功回調')
    },function () {})
  .then(function () {
      console.log('第二個成功回調')
    }, function () {});
    
輸出以下:
任務執行完了
第一個成功回調
第二個成功回調
複製代碼
  • 咱們都熟悉Jquery,它的鏈式調用是用rerun this來作的,但是這裏卻不行,緣由文章末尾再解釋。 咱們採起返回一個新的promise對象來實現鏈式調用.
  • 意思也就是p.then()返回一個新promise對象.
//給這個函數加個返回值,返回值就是一個新new的promise對象
Promise.prototype.then = function (onFulFilled, onRejected) {
    let p2 = new Promise((resolve, reject) => {});
    
    if (this.status === 'pending') {
        this.onFulFilledCallbacks.push(() => {
            onFulFilled(this.value)
        }); 
        this.onRejectedCallbacks.push(() => {
            onRejected(this.reason)
        })
    } else if (this.status === 'resolved') {
        onFulFilled(this.value);
    } else if (this.status === 'rejected') {
        onRejected(this.reason);
    }
    return p2;
}
複製代碼
  • 加了兩行代碼,咱們測試一下輸出結果:
    任務執行完了
    第一個成功回調
  • 根據上一次咱們所講的能夠總結出以下結論:真正觸發onFulFilledCallbacks裏所存儲的回調函數只有兩個地方:
  1. resolve被調用的時候.(resolve是用戶調用的,由於用戶固然知道哪一種邏輯算任務成功,哪一種邏輯算任務失敗,例如咱們上一章的例子,隨機數大於5我就認爲是成功的.)
  2. then被調用的時候.(then也是用戶調用的,promise裏的任務執行完了後要作啥)

這裏咱們理一下流程:在咱們new Promise的時候,executor就同步的執行了,根據executor裏有無異步操做分一下兩種狀況:

1.有異步操做,如咱們的例子裏,有setTimout,延時1.5s後打印。函數

  • executor函數執行完,p也就完成了new的過程。(此時還不會打印輸出任務執行完了,由於setTimout是異步的)
  • 拿到p對象後,代碼接着執行p.then,此時很明顯狀態是pending,咱們會把用戶在then裏傳的回調函數存起來(onFulFilledCallbacks),在1.5s後,setTimeout裏調用resovle的時候,會遍歷onFulFilledCallbacks,執行以前then裏傳的函數
  • 這也就是咱們以前爲何要給promise加三個狀態的緣由(等待pending 成功resolve 失敗rejected),懼怕裏面有異步任務。 無論如何異步,用戶老是知道代碼走到哪兒算是成功了(在成功的地方調resolve),代碼走到哪兒算是出異常了(在失敗的地方調reject)這是規矩,使用Promise必須遵照的規矩。

2.沒有異步操做,如咱們的例子裏,有setTimout,延時1.5s後打印。post

  • 這個就簡單了,跟着代碼順序看就好了,執行executor直接就會調resolve,then的會後,任務已經完成,立即執行用戶傳的回調函數就好了

回到咱們要解決的問題剛只是輸出了第一個成功回調,由於【p2】的status是pending呀(咱們在內部本身new的Promise,內部沒有任何要執行的東西,沒有調resolve,那咱們就調一下唄).

//給這個函數加個返回值,返回值就是一個新new的promise對象
Promise.prototype.then = function (onFulFilled, onRejected) {
    let p2 = new Promise((resolve, reject) => { resove() });
    
    if (this.status === 'pending') {
        this.onFulFilledCallbacks.push(() => {
            onFulFilled(this.value)
        }); 
        this.onRejectedCallbacks.push(() => {
            onRejected(this.reason)
        })
    } else if (this.status === 'resolved') {
        onFulFilled(this.value);
    } else if (this.status === 'rejected') {
        onRejected(this.reason);
    }
    return p2;
}
複製代碼
  • 咱們測試一下輸出結果:
    第二個成功回調
    任務執行完了
    第一個成功回調
  • 這一會卻是執行了,但是順序亂了,緣由:p.then拿到p2,p2馬上就resolve了,當代碼走到p2.then天然就是直接走到this.status === 'resolved',執行傳入的函數了.等1.5s後第一個promsie任務完成,打印任務執行完了調用第一個的resolve,第一個成功回調
  • 咱們想要的是本身控制順序,看直白一點就是咱們得控制在第一個Promise也就是P任務執行完了,才能調P2.resove,那就直接在第一個Promise回調函數執行完了咱們再調P2.resove吧
//給這個函數加個返回值,返回值就是一個新new的promise對象
Promise.prototype.then = function (onFulFilled, onRejected) {
  //p2的resolve在裏面,外面拿不到,只有這樣很賤的給在外面記下來了
  let p2Resolve ;
  let p2Reject;
  let p2 = new Promise((resolve, reject) => {
    p2Resolve = resolve;
    p2Reject = reject;
  });

  if (this.status === 'pending') {
    this.onFulFilledCallbacks.push(() => {
      onFulFilled(this.value)
      p2Resolve()
    });
    this.onRejectedCallbacks.push(() => {
      onRejected(this.reason)
      p2Reject()
    })
  } else if (this.status === 'resolved') {
    onFulFilled(this.value);
    p2Resolve()
  } else if (this.status === 'rejected') {
    onRejected(this.reason);
    p2Reject()
  }

  return p2;
}
複製代碼

輸出以下:
任務執行完了
第一個成功回調
第二個成功回調測試

結語

咱們剩下onFulFilled返回值的功能沒作,下次子再來.
感謝各位觀衆.ui

相關文章
相關標籤/搜索