手寫一個promise

[toc]

本文遵循的Promise/A+規(guī)范實現(xiàn)一個簡略版本Promise, 用代碼理解規(guī)范中的每一句話.

Promise 的狀態(tài)

規(guī)范描述

一個 Promise 的當前狀態(tài)必須為以下三種狀態(tài)中的一種:等待態(tài)(Pending)浊仆、完成態(tài)(Fulfilled)拒絕態(tài)(Rejected)

  • 等待態(tài)(Pending)
    處于等待態(tài)時,promise 需滿足以下條件:
    • 可以遷移至執(zhí)行態(tài)或拒絕態(tài)
  • 完成態(tài)(Fulfilled)
    處于執(zhí)行態(tài)時靖榕,promise 需滿足以下條件:
    • 不能遷移至其他任何狀態(tài)
    • 必須擁有一個不可變的終值
  • 拒絕態(tài)(Rejected)
    處于拒絕態(tài)時蠢笋,promise 需滿足以下條件:
    • 不能遷移至其他任何狀態(tài)
    • 必須擁有一個不可變的據(jù)因

代碼實現(xiàn)

采用類實現(xiàn):

class MyPromise {
}

實現(xiàn)構造函數(shù):

const _ = require('lodash');

const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;

class MyPromise {
  constructor(executor) {
    if (!(this instanceof MyPromise)) {
      throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
    }
    if (!_.isFunction(executor)) {
      throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
    }
    this._status = PENDING;
    this._value = undefined;
    this._thenCallbacks = [];
    this._reason = undefined;
    try {
      executor((...args) => this._resolve(...args), (...args) => this._reject(...args));
    } catch(e) {
      this._reject(e);
    }
  }
}

在構造函數(shù)中, 首先處理函數(shù)邊界, 然后初始化狀態(tài),終值(_value), 拒因(_reason)和then的回調(diào)函數(shù)隊列; 最后調(diào)用傳入的executor.executor中用于控制Promise狀態(tài)的變化.傳遞兩個回調(diào)函數(shù)作為參數(shù)媒殉,第一個參數(shù)叫做resove起惕,第二個參數(shù)叫做 reject, 在對象中的處理處理邏輯為:

_resolve(value) {
    if (this._status !== PENDING) {
      return;
    }
    this._status = FULFILLED;
    this._value = value;
  }

  _reject(reason) {
    if (this._status !== PENDING) {
      return;
    }
    this._status = REJECTED;
    this._reason = reason;
  }

_resolve, _reject有狀態(tài)邊界判斷, 如果被調(diào)用多次, 采用首次調(diào)用并忽略剩下的調(diào)用._resolve中只允許等待態(tài)轉為完成態(tài),然后接收終值._reject中只允許等待態(tài)轉為拒絕態(tài), 然后接收拒因.

狀態(tài)的判斷保證_resolve, _reject中主要邏輯在當前promise中最多被執(zhí)行一次: 狀態(tài)最多改變一次;then的回調(diào)函數(shù)最多調(diào)用一次.

Then函數(shù)

規(guī)范描述

一個promise必須提供一個then方法以訪問其當前值盅粪、終值和據(jù)因柜与。
promisethen方法接受兩個參數(shù):

promise.then(onFulfilled, onRejected)

參數(shù)可選

onFulfilledonRejected都是可選參數(shù)巧勤。

  • 如果onFulfilled不是函數(shù),其必須被忽略
  • 如果onRejected不是函數(shù)弄匕,其必須被忽略

onFulfilled特性

如果onFulfilled是函數(shù):

  • promise執(zhí)行結束后其必須被調(diào)用颅悉,其第一個參數(shù)為promise的終值
  • promise執(zhí)行結束前其不可被調(diào)用
  • 其調(diào)用次數(shù)不可超過一次

調(diào)用時機

onFulfilledonRejected只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用

調(diào)用要求

onFulfilledonRejected必須被作為函數(shù)調(diào)用(即沒有 this 值)注2

多次調(diào)用

then方法可以被同一個promise調(diào)用多次

promise成功執(zhí)行時,所有onFulfilled需按照其注冊順序依次回調(diào)
promise被拒絕執(zhí)行時迁匠,所有的onRejected需按照其注冊順序依次回調(diào)

返回

then方法必須返回一個promise對象

promise2 = promise1.then(onFulfilled, onRejected);   
  • 如果onFulfilled或者onRejected返回一個值 x 剩瓶,則運行下面的Promise解決過程:[[Resolve]](promise2, x)
  • 如果onFulfilled或者onRejected拋出一個異常e,則promise2必須拒絕執(zhí)行城丧,并返回拒因e
  • 如果onFulfilled不是函數(shù)且promise1成功執(zhí)行延曙,promise2必須成功執(zhí)行并返回相同的值
  • 如果 onRejected 不是函數(shù)且promise1拒絕執(zhí)行,promise2必須拒絕執(zhí)行并返回相同的據(jù)因

代碼實現(xiàn)

需要在構造函數(shù)添加回調(diào)函數(shù)隊列:

constructor(executor) {
    // ...
    this._value = undefined;
    this._thenCallbacks = [];
    this._reason = undefined;
    // ...
  }

首先, 實現(xiàn)一個then函數(shù).由于本章節(jié)代碼牽扯到很多部分, 所以盡量用代碼注釋來說明實現(xiàn)的規(guī)范:

then(onFulfilled, onRejected) {
    // 如果 onFulfilled 不是函數(shù)亡哄,其必須被忽略
    const _onFulfilled = _.isFunction(onFulfilled) ? onFulfilled : void 0;
    // 如果 onRejected 不是函數(shù)枝缔,其必須被忽略
    const _onRejected = _.isFunction(onRejected) ? onRejected : void 0;

    // then 方法可以被同一個 promise 調(diào)用多次
    this._thenCallbacks.push([_onFulfilled, _onRejected]);

    return new MyPromise((resolve, reject) => {
      // 等待實現(xiàn)
    });
  }

onFulfilledonRejected, 需要在_resove或者reject時被調(diào)用, 將resolve, reject改造為:

_resolve(value) {
    if (this._status !== PENDING) {
      return;
    }
    this._status = FULFILLED;
    this._value = value;
    // 如果then的回調(diào)函數(shù)onFulfilled, onRejected為函數(shù)的話, 需要
    // 在 promise 執(zhí)行結束前其不可被調(diào)用,當 promise 執(zhí)行結束后其必須被調(diào)用
    // 其調(diào)用次數(shù)不可超過一次
    // 只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用 
    process.nextTick(this._callThenCallbacks);
  }

  _reject(reason) {
    if (this._status !== PENDING) {
      return;
    }
    this._status = REJECTED;
    this._reason = reason;
    // 如果then的回調(diào)函數(shù)onFulfilled, onRejected為函數(shù)的話, 需要
    // 在 promise 執(zhí)行結束前其不可被調(diào)用,當 promise 執(zhí)行結束后其必須被調(diào)用
    // 其調(diào)用次數(shù)不可超過一次
    // 只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用 
    process.nextTick(this._callThenCallbacks);
  }

雖然規(guī)范中沒有說明onFulfilledonRejected必需為微任務(micro-task)還是宏任務(macro-task),但是其他實現(xiàn)基本都是基于微任務.

這里由于本文只是在node環(huán)境實現(xiàn),所以采用process.nextTick來啟用微任務, 為了跨平臺, 一般的Promise實現(xiàn)框架, 都會使用多種方式來實現(xiàn)在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用, 如MutationObserver, MessageChannel, vertx等, 最后可能使用setTimeout實現(xiàn).

按照Promise/A+規(guī)范來說,onFulfilledonRejected只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用, 但是在nodejs或者es6環(huán)境中的Promise對象, 需要像下面的實現(xiàn):

_resolve(value) {
    // 只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用 
    process.nextTick(() => {
      if (this._status !== PENDING) {
        return;
      }
      this._status = FULFILLED;
      this._value = value;
      // 如果then的回調(diào)函數(shù)onFulfilled, onRejected為函數(shù)的話, 需要
      // 在 promise 執(zhí)行結束前其不可被調(diào)用,當 promise 執(zhí)行結束后其必須被調(diào)用
      // 其調(diào)用次數(shù)不可超過一次
      // 只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用 
      // process.nextTick(() => this._callThenCallbacks());
      this._callThenCallbacks();
    });
  }

  _reject(reason) {
    // 只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用 
    process.nextTick(() => {
      if (this._status !== PENDING) {
        return;
      }
      this._status = REJECTED;
      this._reason = reason;
      // 如果then的回調(diào)函數(shù)onFulfilled, onRejected為函數(shù)的話, 需要
      // 在 promise 執(zhí)行結束前其不可被調(diào)用,當 promise 執(zhí)行結束后其必須被調(diào)用
      // 其調(diào)用次數(shù)不可超過一次
      // process.nextTick(() => this._callThenCallbacks());
      this._callThenCallbacks();
    });
  }

對于比較流行的prmise polyfill庫es-promise的實現(xiàn),采用的是上面一種啟用微任務的時機, 對于babel中的墊片core-js中實現(xiàn)的promise, 采用的是下一種啟用時機.

Then函數(shù).返回規(guī)范有復雜的要求,為了實現(xiàn)這些要求, 需要改變上面的then函數(shù)的實現(xiàn):

then(onFulfilled, onRejected) {
    // 如果 onFulfilled 不是函數(shù),其必須被忽略
    const _onFulfilled = _.isFunction(onFulfilled) ? onFulfilled : void 0;
    // 如果 onRejected 不是函數(shù)蚊惯,其必須被忽略
    const _onRejected = _.isFunction(onRejected) ? onRejected : void 0;
    
    let childResolve;
    let childReject;
    const childPromise = new MyPromise((resolve, reject) => {
      childResolve = resolve;
      childReject = reject;
    });

    // then 方法可以被同一個 promise 調(diào)用多次
    this._thenCallbacks.push([_onFulfilled, _onRejected, childResolve, childReject]);

    return childPromise;
  }

_callThenCallbacks用于處理在promise狀態(tài)改變后處理then回調(diào)函數(shù)隊列. 在處理每一個then回調(diào)函數(shù)后, 還需要對于then回調(diào)函數(shù)返回的結果, 結合當前的promise狀態(tài),調(diào)整當前then函數(shù)返回的promise2的狀態(tài):

  // 調(diào)用then回調(diào)函數(shù)隊列
  _callThenCallbacks() {
    if (_.isEmpty(this._thenCallbacks)) {
      return;
    }
    this._thenCallbacks.forEach(([onFulfilled, onRejected, childResolve, childReject]) => {
      try {
        if (this._status === FULFILLED && !onFulfilled) {
          // 如果 onFulfilled 不是函數(shù)且 promise1 成功執(zhí)行愿卸, promise2 必須成功執(zhí)行并返回相同的值
          childResolve(this._value);
          return;
        }
        if (this._status === REJECTED && !onRejected) {
          // 如果 onRejected 不是函數(shù)且 promise1 拒絕執(zhí)行, promise2 必須拒絕執(zhí)行并返回相同的據(jù)因
          childReject(this._reason);
        }
        let x;
        if (this._status === REJECTED && onRejected) {
          // 當 promise 被拒絕執(zhí)行時拐辽,所有的 onRejected 需按照其注冊順序依次回調(diào)
          // 其第一個參數(shù)為 promise 的拒因
          // 必須被作為函數(shù)調(diào)用(即沒有 this 值)
          x = onRejected(this._reason);
        } else if (this._status === FULFILLED && onFulfilled) {
          // 當 promise 成功執(zhí)行時,所有 onFulfilled 需按照其注冊順序依次回調(diào)
          // 其第一個參數(shù)為 promise 的終值
          // 必須被作為函數(shù)調(diào)用(即沒有 this 值)
          x = onFulfilled(this._value);
        }
        // 如果 onFulfilled 或者 onRejected 返回一個值 x 擦酌,則運行下面的 Promise 解決過程
        this._resolvePromise(x, childResolve, childReject);
      } catch (error) {
        childReject(error);
      }
    });
  }

其中_resolvePromise代表Promise解決過程, 將在下文說明.

Promise解決過程

規(guī)范描述

Promise解決過程是一個抽象的操作俱诸,其需輸入一個promise和一個值,我們表示為[[Resolve]](promise, x)赊舶,如果xthen 方法且看上去像一個Promise睁搭,解決程序即嘗試使promise接受x的狀態(tài);否則其用x的值來執(zhí)行 promise 笼平。

這種thenable的特性使得Promise的實現(xiàn)更具有通用性:只要其暴露出一個遵循 Promise/A+ 協(xié)議的then方法即可园骆;這同時也使遵循 Promise/A+ 規(guī)范的實現(xiàn)可以與那些不太規(guī)范但可用的實現(xiàn)能良好共存。

運行[[Resolve]](promise, x)需遵循以下步驟:

  • xpromise相等
    如果x為Promise寓调,則使promise 接受x`的狀態(tài):

    • 如果x處于等待態(tài)锌唾,promise需保持為等待態(tài)直至x被執(zhí)行或拒絕
    • 如果x處于執(zhí)行態(tài),用相同的值執(zhí)行promise
    • 如果x處于拒絕態(tài)夺英,用相同的據(jù)因拒絕promise
  • x為對象或函數(shù)
    如果x為對象或者函數(shù):

    • x.then賦值給then
    • 如果取x.then的值時拋出錯誤e晌涕,則以e為據(jù)因拒絕promise
    • 如果then是函數(shù),將x作為函數(shù)的作用域this調(diào)用之痛悯。傳遞兩個回調(diào)函數(shù)作為參數(shù)余黎,第一個參數(shù)叫做resolvePromise,第二個參數(shù)叫做rejectPromise:
      • 如果resolvePromise以值y為參數(shù)被調(diào)用载萌,則運行[[Resolve]](promise, y)
      • 如果rejectPromise以據(jù)因r為參數(shù)被調(diào)用惧财,則以據(jù)因r拒絕promise
      • 如果resolvePromiserejectPromise均被調(diào)用,或者被同一參數(shù)調(diào)用了多次扭仁,則優(yōu)先采用首次調(diào)用并忽略剩下的調(diào)用
      • 如果調(diào)用then方法拋出了異常e
        • 如果resolvePromiserejectPromise已經(jīng)被調(diào)用垮衷,則忽略之
        • 否則以e為據(jù)因拒絕promise
      • 如果then不是函數(shù),以x為參數(shù)執(zhí)行promise
  • 如果x不為對象或者函數(shù)乖坠,以x為參數(shù)執(zhí)行promise`

如果一個promise被一個循環(huán)的thenable鏈中的對象解決搀突,而[[Resolve]](promise, thenable)的遞歸性質(zhì)又使得其被再次調(diào)用,根據(jù)上述的算法將會陷入無限遞歸之中瓤帚。算法雖不強制要求描姚,但也鼓勵施者檢測這樣的遞歸是否存在,若檢測到存在則以一個可識別的TypeError為據(jù)因來拒絕promise.

代碼實現(xiàn)

函數(shù)_resolvePromise實現(xiàn), 采用代碼注釋說明:

// Promise 解決過程
  _resolvePromise(x, childResolve, childReject) {
    // x 與 promise 相等
    if (x === this) {
      // 如果 promise 和 x 指向同一對象戈次,以 TypeError 為據(jù)因拒絕執(zhí)行 promise
      throw new TypeError("You cannot resolve a promise with itself");
    }
    // 如果 x 為 Promise 轩勘,則使 promise 接受 x 的狀態(tài)
    if (x instanceof MyPromise) {
      // 如果 x 處于等待態(tài)
      console.log('======PENDING===', x._status);
      if (x._status === PENDING) {
        // promise 需保持為等待態(tài)直至 x 被執(zhí)行或拒絕
        x.then(childResolve, childReject);
        return;
      }
      // 如果 x 處于執(zhí)行態(tài)
      if (x._status === FULFILLED) {
        // 用相同的值執(zhí)行 promise
        childResolve(x._value);
        return;
      }
      // 如果 x 處于執(zhí)行態(tài)
      if (x._status === REJECTED) {
        // 用相同的值執(zhí)行 promise
        childReject(x._reason);
        return;
      }
    }
    // x 為對象或函數(shù)
    if (_.isObject(x) || _.isFunction(x)) {
      // 把 x.then 賦值給 then
      let then;
      try {
        then = x.then;
      } catch (error) {
        // 如果取 x.then 的值時拋出錯誤 e ,則以 e 為據(jù)因拒絕 promise
        // 其實這里不需要捕獲, 因為最外層有捕獲, 這里為了保持跟規(guī)范一致
        childReject(error);
        return;
      }
      // 如果 then 是函數(shù)
      if (_.isFunction(then)) {
        // 將 x 作為函數(shù)的作用域 this 調(diào)用之
        let called = false;
        try {
          then.call(x, (y) => {
            // 如果 resolvePromise 和 rejectPromise 均被調(diào)用怯邪,或者被同一參數(shù)調(diào)用了多次,
            // 則優(yōu)先采用首次調(diào)用并忽略剩下的調(diào)用
            if (called) {
              return;
            }
            called = true;
  
            // 如果 resolvePromise 以值 y 為參數(shù)被調(diào)用
            this._resolvePromise(y, childResolve, childReject);
          }, (r) => {
            // 如果 resolvePromise 或 rejectPromise 已經(jīng)被調(diào)用绊寻,則忽略之
            if (called) {
              return;
            }
            called = true;
  
            // 如果 rejectPromise 以據(jù)因 r 為參數(shù)被調(diào)用,則以據(jù)因 r 拒絕 promise
            childReject(r);
          }); 
        } catch (error) {
          // 如果調(diào)用 then 方法拋出了異常 e

          // 如果 resolvePromise 或 rejectPromise 已經(jīng)被調(diào)用,則忽略之
          if (called) {
            return;
          }

          // 否則以 e 為據(jù)因拒絕 promise
          childReject(error);
        }
        return;
      }
      // 如果 then 不是函數(shù), 以 x 為參數(shù)執(zhí)行 promise
      childResolve(x);
      return;
    }
    // 如果 x 不為對象或者函數(shù), 以 x 為參數(shù)執(zhí)行 promise
    childResolve(x);
  }

全部代碼

const _ = require('lodash');

const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;

class MyPromise {
  constructor(executor) {
    if (!(this instanceof MyPromise)) {
      throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
    }
    if (!_.isFunction(executor)) {
      throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
    }
    this._status = PENDING;
    this._value = undefined;
    this._thenCallbacks = [];
    this._reason = undefined;
    try {
      executor((...args) => this._resolve(...args), (...args) => this._reject(...args));
    } catch(e) {
      this._reject(e);
    }
  }

  _resolve(value) {
    // 只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用 
    process.nextTick(() => {
      if (this._status !== PENDING) {
        return;
      }
      this._status = FULFILLED;
      this._value = value;
      // 如果then的回調(diào)函數(shù)onFulfilled, onRejected為函數(shù)的話, 需要
      // 在 promise 執(zhí)行結束前其不可被調(diào)用,當 promise 執(zhí)行結束后其必須被調(diào)用
      // 其調(diào)用次數(shù)不可超過一次
      // 只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用 
      // process.nextTick(() => this._callThenCallbacks());
      this._callThenCallbacks();
    });
  }

  _reject(reason) {
    // 只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用 
    process.nextTick(() => {
      if (this._status !== PENDING) {
        return;
      }
      this._status = REJECTED;
      this._reason = reason;
      // 如果then的回調(diào)函數(shù)onFulfilled, onRejected為函數(shù)的話, 需要
      // 在 promise 執(zhí)行結束前其不可被調(diào)用,當 promise 執(zhí)行結束后其必須被調(diào)用
      // 其調(diào)用次數(shù)不可超過一次
      // 只有在執(zhí)行環(huán)境堆棧僅包含平臺代碼時才可被調(diào)用 
      // process.nextTick(() => this._callThenCallbacks());
      this._callThenCallbacks();
    });
  }

  // 調(diào)用then回調(diào)函數(shù)隊列
  _callThenCallbacks() {
    if (_.isEmpty(this._thenCallbacks)) {
      return;
    }
    this._thenCallbacks.forEach(([onFulfilled, onRejected, childResolve, childReject]) => {
      try {
        if (this._status === FULFILLED && !onFulfilled) {
          // 如果 onFulfilled 不是函數(shù)且 promise1 成功執(zhí)行澄步, promise2 必須成功執(zhí)行并返回相同的值
          childResolve(this._value);
          return;
        }
        if (this._status === REJECTED && !onRejected) {
          // 如果 onRejected 不是函數(shù)且 promise1 拒絕執(zhí)行, promise2 必須拒絕執(zhí)行并返回相同的據(jù)因
          childReject(this._reason);
        }
        let x;
        if (this._status === REJECTED && onRejected) {
          // 當 promise 被拒絕執(zhí)行時冰蘑,所有的 onRejected 需按照其注冊順序依次回調(diào)
          // 其第一個參數(shù)為 promise 的拒因
          // 必須被作為函數(shù)調(diào)用(即沒有 this 值)
          x = onRejected(this._reason);
        } else if (this._status === FULFILLED && onFulfilled) {
          // 當 promise 成功執(zhí)行時,所有 onFulfilled 需按照其注冊順序依次回調(diào)
          // 其第一個參數(shù)為 promise 的終值
          // 必須被作為函數(shù)調(diào)用(即沒有 this 值)
          x = onFulfilled(this._value);
        }
        // 如果 onFulfilled 或者 onRejected 返回一個值 x 村缸,則運行下面的 Promise 解決過程
        this._resolvePromise(x, childResolve, childReject);
      } catch (error) {
        childReject(error);
      }
    });
  }

  // Promise 解決過程
  _resolvePromise(x, childResolve, childReject) {
    // x 與 promise 相等
    if (x === this) {
      // 如果 promise 和 x 指向同一對象祠肥,以 TypeError 為據(jù)因拒絕執(zhí)行 promise
      throw new TypeError("You cannot resolve a promise with itself");
    }
    // 如果 x 為 Promise ,則使 promise 接受 x 的狀態(tài)
    if (x instanceof MyPromise) {
      // 如果 x 處于等待態(tài)
      console.log('======PENDING===', x._status);
      if (x._status === PENDING) {
        // promise 需保持為等待態(tài)直至 x 被執(zhí)行或拒絕
        x.then(childResolve, childReject);
        return;
      }
      // 如果 x 處于執(zhí)行態(tài)
      if (x._status === FULFILLED) {
        // 用相同的值執(zhí)行 promise
        childResolve(x._value);
        return;
      }
      // 如果 x 處于執(zhí)行態(tài)
      if (x._status === REJECTED) {
        // 用相同的值執(zhí)行 promise
        childReject(x._reason);
        return;
      }
    }
    // x 為對象或函數(shù)
    if (_.isObject(x) || _.isFunction(x)) {
      // 把 x.then 賦值給 then
      let then;
      try {
        then = x.then;
      } catch (error) {
        // 如果取 x.then 的值時拋出錯誤 e 梯皿,則以 e 為據(jù)因拒絕 promise
        // 其實這里不需要捕獲, 因為最外層有捕獲, 這里為了保持跟規(guī)范一致
        childReject(error);
        return;
      }
      // 如果 then 是函數(shù)
      if (_.isFunction(then)) {
        // 將 x 作為函數(shù)的作用域 this 調(diào)用之
        let called = false;
        try {
          then.call(x, (y) => {
            // 如果 resolvePromise 和 rejectPromise 均被調(diào)用仇箱,或者被同一參數(shù)調(diào)用了多次,
            // 則優(yōu)先采用首次調(diào)用并忽略剩下的調(diào)用
            if (called) {
              return;
            }
            called = true;
  
            // 如果 resolvePromise 以值 y 為參數(shù)被調(diào)用
            this._resolvePromise(y, childResolve, childReject);
          }, (r) => {
            // 如果 resolvePromise 或 rejectPromise 已經(jīng)被調(diào)用,則忽略之
            if (called) {
              return;
            }
            called = true;
  
            // 如果 rejectPromise 以據(jù)因 r 為參數(shù)被調(diào)用东羹,則以據(jù)因 r 拒絕 promise
            childReject(r);
          }); 
        } catch (error) {
          // 如果調(diào)用 then 方法拋出了異常 e

          // 如果 resolvePromise 或 rejectPromise 已經(jīng)被調(diào)用剂桥,則忽略之
          if (called) {
            return;
          }

          // 否則以 e 為據(jù)因拒絕 promise
          childReject(error);
        }
        return;
      }
      // 如果 then 不是函數(shù), 以 x 為參數(shù)執(zhí)行 promise
      childResolve(x);
      return;
    }
    // 如果 x 不為對象或者函數(shù), 以 x 為參數(shù)執(zhí)行 promise
    childResolve(x);
  }

  then(onFulfilled, onRejected) {
    // 如果 onFulfilled 不是函數(shù),其必須被忽略
    const _onFulfilled = _.isFunction(onFulfilled) ? onFulfilled : void 0;
    // 如果 onRejected 不是函數(shù)属提,其必須被忽略
    const _onRejected = _.isFunction(onRejected) ? onRejected : void 0;
    
    let childResolve;
    let childReject;
    const childPromise = new MyPromise((resolve, reject) => {
      childResolve = resolve;
      childReject = reject;
    });

    // then 方法可以被同一個 promise 調(diào)用多次
    this._thenCallbacks.push([_onFulfilled, _onRejected, childResolve, childReject]);

    return childPromise;
  }
}

module.exports = MyPromise;
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末权逗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子冤议,更是在濱河造成了極大的恐慌斟薇,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件求类,死亡現(xiàn)場離奇詭異奔垦,居然都是意外死亡,警方通過查閱死者的電腦和手機尸疆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惶岭,“玉大人寿弱,你說我怎么就攤上這事“丛睿” “怎么了症革?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鸯旁。 經(jīng)常有香客問我噪矛,道長,這世上最難降的妖魔是什么铺罢? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任艇挨,我火速辦了婚禮,結果婚禮上韭赘,老公的妹妹穿的比我還像新娘缩滨。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布脉漏。 她就那樣靜靜地躺著苞冯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侧巨。 梳的紋絲不亂的頭發(fā)上舅锄,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音司忱,去河邊找鬼巧娱。 笑死,一個胖子當著我的面吹牛烘贴,可吹牛的內(nèi)容都是我干的禁添。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼桨踪,長吁一口氣:“原來是場噩夢啊……” “哼老翘!你這毒婦竟也來了?” 一聲冷哼從身側響起锻离,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤铺峭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后汽纠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卫键,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年虱朵,在試婚紗的時候發(fā)現(xiàn)自己被綠了莉炉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡碴犬,死狀恐怖絮宁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情服协,我是刑警寧澤绍昂,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站偿荷,受9級特大地震影響窘游,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜跳纳,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一忍饰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧棒旗,春花似錦喘批、人聲如沸撩荣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽腻格。三九已至搏讶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背竹挡。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工做入, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赎线,地道東北人痛侍。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像宪彩,于是被迫代替她去往敵國和親休讳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355