1. 程式人生 > >ES6——手把手實現一個簡單的Promise

ES6——手把手實現一個簡單的Promise

想要實現一個Promise,首先當然得先知道這個東西是什麼,Promise物件是ES6給我們提供的一個解決非同步回撥的一個語法糖,簡單說就是一個容器,裡面儲存著某個未來才會結束的事件(通常是一個非同步操作)的結果。把非同步操作放入Promise物件中,等到Promise物件中的操作完成了,再繼續執行then裡面的操作。具體的使用就不說了,大概的使用方法如同下面。詳情可以看ES6

    let p=new Promise(function (resolve,reject) {
        setTimeout(function () {
            resolve(2);
        },1000);
    });
    p.then(function (n) {
        console.log(n);
    },function (n) {
        console.log(n);
    });

那麼接下來就來具體說說要怎麼去實現一個Promise物件,這裡我先用es5的語法實現一下,後面會附帶用es6的語法,也就是用到了“類”這個概念

首先按照es5建立物件的套路,先來個初始化

 function Promise2(fn) {
    const _this = this;    //方便this改變時,能夠繼續呼叫
    this._queue = [];    //promise關鍵,一個函式佇列,用於儲存為能夠開始執行的函式
    this._succ_res = null;     //用於存放成功時的引數
    this._err_res = null;    //用於存放失敗時的引數
    this.status = '';    //Promise狀態
    fn();     //傳入Promise的回撥函式
  }

我們知道Promise傳進來的引數是一個函式,那麼最主要的事情就是要在Promise裡面去執行它。執行完之後呢,這個函式可能是執行成功的,也可能失敗,所以如下圖,我們可以看到有一個resolve和reject兩個方法。因此我們需要在傳進來的引數裡面設定兩個function。

function Promise2(fn) {
    ......
    fn(function (...arg) {
      //resolve
    }, function (...arg) {
      //reject
    })
  }

同時我們還有一個then的方法,這個時候就要搬出我們的原型了。那麼then裡面要做什麼呢?在then裡面,會被放入兩個函式,一個表示成功後執行的,一個表示失敗後執行的,所以then主要是要做下面的事

  1. 判斷當前Promise的狀態,Promise有3個狀態,pending、fulfilled和rejected。簡單講就是,Promise的函式還沒執行完、Promise的函式執行完了並且成功了、Promise執行完了但失敗了,這三種狀態。
  2. 如果未執行完:把then裡面要執行的函式放在等待佇列裡面
  3. 如果執行完且成功:執行then中表示成功的函式
  4. 如果執行完但失敗:執行then中表示失敗的函式

所以Promise的then如下

Promise2.prototype={
    then:function (fn1, fn2) {
      let _this=this;
      if (_this.status == 'success') {
        fn1(..._this._succ_res);
      } else if (_this.status == 'error') {
        fn2(..._this._succ_res);
      } else {
        _this._queue.push({fn1, fn2});
      }
    }
  }

同樣是我們在原來的函式裡面,也要對狀態的改變進行設定。而狀態一旦確定下來也就不會再改變,那麼,也就要先判斷狀態是否已經發生改變

fn(function (...arg) {
      //resolve
      if (_this.status != 'error') {    //判斷狀態是否已經變成失敗
        _this.status = 'success';    //改變狀態
        _this._succ_res = arg;    //傳入resolve()中傳給then的引數
        _this._queue.forEach(json => {    //執行結束後,看看佇列裡是否有函式,有的話執行第一個,並傳入對應的引數
          json.fn1(...arg);
        });
      }
    }, function (...arg) {
      //reject
      if (_this.status != 'success') {    //判斷狀態是否已經變成成功
        _this.status = 'error';    //改變狀態
        _this._err_res = arg;    //傳入resolve()中傳給then的引數
        _this._queue.forEach(json => {    //執行結束後,看看佇列裡是否有函式,有的話執行第二個函式,並傳入對應的引數
          json.fn2(...arg);
        });
      }
    })

完整程式碼如下

  function Promise2(fn) {
    const _this = this;
    this._queue = [];
    this._succ_res = null;
    this._err_res = null;
    this.status = '';
    fn(function (...arg) {
      //resolve
      if (_this.status != 'error') {
        _this.status = 'success';
        _this._succ_res = arg;
        _this._queue.forEach(json => {
          json.fn1(...arg);
        });
      }
    }, function (...arg) {
      //reject
      if (_this.status != 'success') {
        _this.status = 'error';
        _this._err_res = arg;
        _this._queue.forEach(json => {
          json.fn2(...arg);
        });
      }
    })
  }
  Promise2.prototype={
    then:function (fn1, fn2) {
      let _this=this;
      if (_this.status == 'success') {
        fn1(..._this._succ_res);
      } else if (_this.status == 'error') {
        fn2(..._this._succ_res);
      } else {
        _this._queue.push({fn1, fn2});
      }
    }
  }


  let p2=new Promise2(function (resolve,reject) {
      setTimeout(function () {
          resolve(22);
      },1000);
      setTimeout(function () {
          reject(23);
      },500);
  });
  p2.then(function (n) {
    console.log(n);
  }, function (n) {
    console.log(n);
  })

最後來總結一下

Promise之所以能夠把非同步的東西按照同步的形式去執行,無非因為兩點

  1. 當狀態未發生改變時,能夠將then中的函式暫時掛起
  2. 當狀態改變時,能夠呼叫其之前掛起的then佇列