Promise的實現(step by step)

24號發表於2018-05-01

閒來無事,對著阮一峰的《es6入門教程》中promise章節,僅僅觀察其表象和特性,一步步實現了Promise這個黑箱子,實現了文中描述的大部分功能與特性,並且還將持續完善,權當磨練技術。

以下是較為完善的版本。

class _Promise {
  constructor(callback) {
    this.status = 'pending';
    this.funcArr = [];
    try {
      callback(params => {
        setTimeout(() => {
          if (this.status != 'pending') return;
          this.status = 'resolved';
          // result儲存resolve傳遞的引數
          let result = params;
          if (result instanceof _Promise) {
            for (let i = 0; i < this.funcArr.length; i++) {
              const func = this.funcArr[i];
              if (func.type === 'suc') {
                result.then(func);
              } else {
                result.catch(func);
              }
            }
          } else if (result instanceof Error) {
            for (let i = 0; i < this.funcArr.length; i++) {
              const func = this.funcArr[i];
              if (func.type === 'fail') {
                try {
                  result = func(result);
                } catch (e) {
                  result = e;
                }
                const _p = _Promise.resolve(result);
                for (let j = i + 1; j < this.funcArr.length; j++) {
                  const func = this.funcArr[j];
                  if (func.type === 'suc') {
                    _p.then(func);
                  } else {
                    _p.catch(func);
                  }
                }
                break;
              }
            }
          } else {
            for (let i = 0; i < this.funcArr.length; i++) {
              const func = this.funcArr[i];
              if (func.type === 'suc') {
                try {
                  result = func(result);
                } catch (e) {
                  result = e;
                }
                const _p = _Promise.resolve(result);
                for (let j = i + 1; j < this.funcArr.length; j++) {
                  const func = this.funcArr[j];
                  if (func.type === 'suc') {
                    _p.then(func);
                  } else {
                    _p.catch(func);
                  }
                }
                break;
              }
            }
          }

        }, 0)
      }, params => {
        setTimeout(() => {
          if (this.status != 'pending') return;

          this.status = 'rejected';
          let result = params;
          if (result instanceof _Promise) {
            for (let i = 0; i < this.funcArr.length; i++) {
              const func = this.funcArr[i];
              if (func.type === 'suc') {
                result.then(func);
              } else {
                result.catch(func);
              }
            }
          } else {
            for (let i = 0; i < this.funcArr.length; i++) {
              const func = this.funcArr[i];
              if (func.type === 'fail') {
                try {
                  result = func(result);
                } catch (e) {
                  result = e;
                }
                const _p = _Promise.resolve(result);
                for (let j = i + 1; j < this.funcArr.length; j++) {
                  const func = this.funcArr[j];
                  if (func.type === 'suc') {
                    _p.then(func);
                  } else {
                    _p.catch(func);
                  }
                }
                break;
              }
            }
          }

        }, 0)
      })
    } catch (err) {
      setTimeout(() => {
        if (this.status != 'pending') return;
        this.status = 'rejected';
        let result = err;
        let hasCatch = false;

        for (let i = 0; i < this.funcArr.length; i++) {
          const func = this.funcArr[i];
          if (func.type === 'fail') {
            try {
              result = func(result);
            } catch (e) {
              result = e;
            }
            const _p = _Promise.resolve(result);
            for (let j = i + 1; j < this.funcArr.length; j++) {
              const func = this.funcArr[j];
              if (func.type === 'suc') {
                _p.then(func);
              } else {
                _p.catch(func);
              }
            }
            hasCatch = true;
            break;
          }
        }

        if (!hasCatch) {
          throw result;
        }

      }, 0)
    }

  }

  then(suc, fail) {
    if (typeof suc === 'function') {
      suc.type = 'suc';
      this.funcArr.push(suc);
    }
    if (typeof fail === 'function') {
      fail.type = 'fail';
      this.funcArr.push(fail);
    }

    return this;
  }

  catch(fail) {
    if (typeof fail === 'function') {
      fail.type = 'fail';
      this.funcArr.push(fail);
    }

    return this;
  }

  finally(callback) {
    this.then(() => {
      callback();
    }, () => {
      callback();
    });
    return this;
  }
}

_Promise.resolve = function (params) {
  if (params instanceof _Promise) {
    return params;
  } else {
    return new _Promise(resolve => {
      resolve(params);
    })
  }
}

_Promise.reject = function (params) {
  return new _Promise((resolve, reject) => {
    reject(params);
  })
}

_Promise.all = function (params) {
  return new _Promise((resolve, reject) => {
    let results = [];
    let count = 0;

    params.forEach((p, i) => {
      results[i] = null;
      _Promise.resolve(p).then(res => {
        results[i] = res;
        count++;
        if (count === results.length) {
          resolve(results);
        }
      }).catch(err => {
        reject(err);
      })
    })
  })
}

export default _Promise
複製程式碼

因為寫程式碼本身是個思維很跳躍的過程,所以具體的實現步驟就不描述了,但是我的github倉庫裡保留了每一個小里程碑的原始碼記錄,如果有興趣可以檢視,這樣就能大概還原整個編碼過程的心路歷程。

github倉庫--Promise的簡單實現

相關文章