快速掌握Promise

前言:

面試會遇到Promise,問你是怎麼理解Promise的?git

個人理解就是承諾!es6

編碼代碼,存在異步的地方都會遇到Promise! 搞了這麼久,感受理解不是很透github

突然想起一句話:學習法語糖的最好方法,是本身去實現ta,本身實現不了,就看比人怎麼實現的面試

基本用法:

let promise = new Promise(   function(resolve, reject) {
/* 異步操做 */  setTimeout(() => {
    let number = Math.floor(Math.random() * 3)
    if (number == 2) {
      resolve(number);
    } else {
      reject('不是2')
    }
  }, 3000);

}     )
promise.then( 
(value) => {// then方法第一個參數onFulfilled,異步任務成功時調用
  console.log(value);
}, 
(value) => {// then方法第二個參數onRejected,異步任務成功時調用
  console.log(value);
}
);
複製代碼

根據上面的內容開始實現 Promise構造函數

function Promise(fn) {
    //校驗
  if (!(this instanceof Promise))
    throw new TypeError('Promise 前面必須加new');
  if (typeof fn !== 'function') throw new TypeError('not a function');

  this._state = 0; // _state有三個值 0表明PENDING(進行中) 
                   //1表明FULFILLED (已成功) 
                   //2表明REJECTED(已失敗)
                   //3表明傳入resolve的是Promise實例
  this._value = undefined; //異步任務完成獲取的值 reslove()或者reject()中傳遞的值
  this._deferreds = []; //存放then函數傳遞的回調

  doResolve(fn, this);
}
複製代碼

根據上面的例子能夠看到Promise構造函數的參數是一個包含異步任務的函數數組

有了構造函數,接下來實現doResolve(fn, this),doResolve要作兩件事,第一要執行咱們傳入的promise

包含異步任務的函數,同時傳入resolve,reject參數 function(resolve, reject) {,能夠看上線的例子bash

function doResolve(fn, self) {
  var done = false;
    fn(
      function(value) { //第一個參數resolve,當異步成功完成調用
        if (done) return;
        done = true;
        resolve(self, value);
      }, 
      function(reason) { //第二個參數reject,當異步完成失敗調用
        if (done) return;
        done = true;
        reject(self, reason);
      }
    );
}
複製代碼

其中利用閉包,保證傳入的resolve,reject 只能調用一次,保持對self,Promise實例的引用閉包

接下來實現resolve,reject,方法只是記錄下_state的狀態dom

function resolve(self, newValue) {
    self._state = 1;
    self._value = newValue;
    finale(self);
}

function reject(self, newValue) {
  self._state = 2;
  self._value = newValue;
  finale(self);
}複製代碼

實現finale,調用self._deferreds 存放then方法中傳入的回調onFulfilled和onRejected異步

再沒有調用then方法時,self._deferreds=[],當調用then 方法時判斷self._state === 0是沒有完成

就將then 方法的回調存入self._deferreds,若是已經完成,根據self._state爲1或者2調用onFulfilled或者onRejected

function finale(self) {
  for (var i = 0, len = self._deferreds.length; i < len; i++) {
    handle(self, self._deferreds[i]);
  }
  self._deferreds = null;
}


function handle(self, deferred) {
  if (self._state === 0) {
    self._deferreds.push(deferred);
    return;
  }
  setTimeout(function() {
    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
    cb(self._value);
  }, 0);
}複製代碼

實現then方法,當調用then 方法時判斷self._state === 0是沒有完成

就將then 方法的回調存入self._deferreds,若是已經完成,根據self._state爲1或者2調用onFulfilled或者onRejected

Promise.prototype.then = function(onFulfilled, onRejected) {
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
  onRejected = typeof onRejected === 'function' ? onRejected : null;

  handle(this, {onFulfilled,onRejected});
}複製代碼

基本的Promise就實現了,去掉不少細節

剩下的就是Promise的幾個方法實現

Promise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected);
};

Promise.resolve = function(value) {
  if (value && typeof value === 'object' && value.constructor === Promise) {
    return value;
  }

  return new Promise(function(resolve) {
    resolve(value);
  });
};

Promise.reject = function(value) {
  return new Promise(function(resolve, reject) {
    reject(value);
  });
};

Promise.race = function(values) {
  return new Promise(function(resolve, reject) {
    for (var i = 0, len = values.length; i < len; i++) {
      values[i].then(resolve, reject);
    }
  });
};
複製代碼

這幾個方法都很簡單,複雜一點的是Promise.all;

Promise.all = function(arr) {
  return new Promise(function(resolve, reject) {
    if (!arr || typeof arr.length === 'undefined')
      throw new TypeError('Promise.all accepts an array');
    var args = Array.prototype.slice.call(arr);
    if (args.length === 0) return resolve([]);
    var remaining = args.length;

    function res(i, val) {
      try {
        if (val && (typeof val === 'object' || typeof val === 'function')) {
          var then = val.then;
          if (typeof then === 'function') {
            then.call(
              val,
              function(val) {
                res(i, val);
              },
              reject
            );
            return;
          }
        }
        args[i] = val;
        if (--remaining === 0) {
          resolve(args);
        }
      } catch (ex) {
        reject(ex);
      }
    }

    for (var i = 0; i < args.length; i++) {
      res(i, args[i]);
    }
  });

複製代碼

Promise.all傳入的是一個Promise實例數組,經過計數方式當傳入的Promise實例每完成一個--remaining,計數器減一,全部都完成了調用resolve觸發then的回調

當你看完實現代碼再結合阮一峯的講解實例es6.ruanyifeng.com/#docs/promi…,應該能夠更快的掌握


本文爲了更好理解,省去了不少代碼完整代碼請查看promise-polyfill 

相關文章
相關標籤/搜索