天天看點

手寫Promise (自定義Promise) —— Promise從入門到自定義筆記

一、自定義Promise

Promise.js檔案

/**
 * 自定義Promise函數子產品: ES5如何自定義子產品?IIFE 匿名函數自調用;函數自調用
 */
(function(window) {
  const PENDING = "pending";
  const RESOLVED = "resolved";
  const REJECTED = "rejected";
  /**
   * Promise 構造函數 ;excutor 執行器函數 (同步執行);
   */
  function Promise(excutor) {
    // 将目前promise對象儲存起來;
    const self = this;
    self.status = PENDING; // 給promsie指定status,初始為pendding;
    self.data = undefined; // 給promise指定一個存儲資料的屬性
    self.callbacks = []; // 每個元素的結構:{onResolved(){},onRejected(){}}
    /** 用于改變promise狀态 */
    function resolve(value) {
      // 如果目前狀态不是pending,直接結束;因為 狀态隻能改變一次,status為pending的時候,才能繼續往下執行;
      if (self.status !== PENDING) return;
      // 1. status狀态改為resolved
      self.status = RESOLVED;
      // 2. 儲存value資料
      self.data = value;
      // 如果有待執行的callback函數,立即異步執行(需放入隊列中,這裡借助setTimeout)回調函數onResolved;
      if (self.callbacks.length > 0) {
        setTimeout(() => {
          // 放入隊列中,執行所有成功的回調
          self.callbacks.forEach(callbacksObj => {
            callbacksObj.onResolved(value);
          });
        });
      }
    }

    function reject(reason) {
      // 如果目前狀态不是pending,直接結束;因為 狀态隻能改變一次,status為pending的時候,才能繼續往下執行;
      if (self.status !== PENDING) return;
      // 1. status狀态改為rejected
      self.status = REJECTED;
      // 2. 儲存value資料
      self.data = reason;
      // 如果有待執行的callback函數,立即異步執行(需放入隊列中,這裡借助setTimeout)回調函數onResolved;
      if (self.callbacks.length > 0) {
        setTimeout(() => {
          // 放入隊列中,執行所有成功的回調
          self.callbacks.forEach(callbacksObj => {
            callbacksObj.onRejected(reason);
          });
        });
      }
    }

    // 立即同步執行excutor
    try {
      excutor(resolve, reject);
    } catch (error) {
      reject(error); // 如果執行器抛出異常,promise變為失敗狀态,故調用reject
    }
  }

  /**
   * Promise原型對象的方法then();
   * 指定成功或者失敗的回調
   * 傳回一個promise對象
   */
  Promise.prototype.then = function(onResolved, onRejected) {
    const self = this;
    // 指定回調函數的預設值,調用.then的時候,可能不執行onRejected
    onResolved = typeof onResolved === "function" ? onResolved : value => value; // 向後傳遞成功的value
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : reason => {
            throw reason; // 指定預設的失敗的回調,将異常傳下去;(實作錯誤/異常傳透);向後傳遞失敗的reason
          };

    // 傳回一個新的promise
    return new Promise((resolve, reject) => {
      function handler(callback) {
        // 根據執行的結果,改變return的promise狀态;
        /**
         * onResolved或者onRejected 的結果有三種情況:
         * 1.抛出異常,return的promise就會失敗,狀态為rejected狀态,reason就是error
         * 2.傳回一個非promise,return的promise就會成功,狀态為resolved狀态,value就是傳回的值;
         * 3.傳回一個promise,return的promise的結果就是該promise的結果;
         */

        try {
          const result = callback(self.data);
          if (result instanceof Promise) {
            // result.then(
            //   value => {
            //     resolve(value);
            //   },
            //   reason => {
            //     reject(reason);
            //   }
            // );
            result.then(resolve, reject);
          } else {
            resolve(result);
          }
        } catch (error) {
          reject(error);
        }
      }

      if (self.status === PENDING) {
        // pending,将回調函數存起來,等待被執行;并讓它影響return的promise的狀态;

        self.callbacks.push({
          onResolved(value) {
            // value,這個參數要不要都可
            handler(onResolved);
          },
          onRejected(reason) {
            handler(onRejected);
          }
        });
      } else if (self.status === RESOLVED) {
        // resolved,異步執行onResolved函數,并改變return的promise狀态;
        setTimeout(() => {
          handler(onResolved);
        });
      } else {
        // rejected,異步執行onRejected函數
        setTimeout(() => {
          handler(onRejected);
        });
      }
    });
  };

  /**
   * Promise 原型對象的catch()方法;
   * 指定失敗的回調;
   * 傳回一個promise
   */
  Promise.prototype.catch = function(onRejected) {
    return this.then(undefined, onRejected);
  };

  // Promise原型鍊上的方法finally(onFinally) ;無論promise是fulfilled或者是rejected,都會執行指定的回調函數onFinally
 /**
   *本質上是有個then方法;且finally要在要在promise狀态改變之後才執行
   * 傳回promise,傳回的promsie的成功與否,取決于onFinally的結果;
   * 而onFinally的執行結果,應該取決于前一個p 或者.then的執行結果;
   * 這裡有個很重要的點,finally表示的是一定會執行,但并不一定是最後執行;
   * 是以在finally中,應該将p的value或者reason向後傳遞下去;
   */
  Promise.prototype.finally = function(onFinally) {
   onFinally = typeof onFinally === "function" ? onFinally : function() {};
    return this.then(
      value => {
        return Promise.resolve(onFinally()).then(() => value); // 向後傳遞成功的value (調用finally的promsie的成功的value)
      },
      reason => {
        return Promise.resolve(onFinally()).then(() => {
          // 實作異常傳透;向後傳遞失敗的reason
          throw reason;
        });
      }
    );
  };

  /**
   * Promise 函數對象的resolve方法;
   * 傳回一個成功或者失敗的promise
   */
  Promise.resolve = function(data) {
    /**
     * 1. 如果接收的是一般值,promise成功,value就是這個值;
     * 2. 如果結束皮的是成功的promise,則promise成功,value是該promise的value;
     * 3. 如果接收的是一個失敗的promsie,則promsie失敗,reason是該promsie的reason
     */
    return new Promise((resolve, reject) => {
      if (data instanceof Promise) {
        data.then(resolve, reject);
      } else {
        resolve(data);
      }
    });
  };

  /**
   * Promise 函數對象的reject方法;
   * 傳回指定結果的一個失敗的promise;
   */
  Promise.reject = function(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  };

  /**
   * Promise 函數對象的all(promises)方法;promises數組中的元素并不一定都是promsie,也可能隻是一個普通的值,如:5
   * 傳回一個promise,當所有的promise都成功的時候,該promise 才是成功狀态;如果成功,傳回所有成功的promise;如果失敗,傳回失敗的reason;
   */
  Promise.all = function(promises) {
    return new Promise((resolve, reject) => {
      const values = new Array(promises.length); //  建立一個和promises長度相同的values數組;用于儲存成功的value 資料;
      let count = 0; // 用來儲存成功的promsie的個數
      // 周遊promises擷取每個promise的結果
      promises.forEach((p, index) => {
        // 這裡将的p不一定是一個promsie,故需将其包裝成一個promsie;如果它是一個promsie,再包一層也無所謂; Promise.resolve(p)
        Promise.resolve(p).then(
          value => {
            count++;
            // 把value按順序放入values中;;将value放入values中,且這裡存的順序應和傳入的promsies中的順序一緻;(數組中的元素和傳入all方法中的promsies中的順序是一樣的)
            values[index] = value;
            // 所有的成功了,将return的promise改為成功;
            if (promises.length === count) {
              resolve(values);
            }
          },
          reason => {
            reject(reason);
          }
        );
      });
    });
  };

  /**
   * Promise函數對象的race方法;
   * 傳回一個promise,傳回最先完成的那個promise,無論他成功還是失敗;
   */
  Promise.race = function(promises) {
    return new Promise((resolve, reject) => {
      // 周遊promises擷取每個promise的結果;因為promise的狀态隻能改變一次,故,隻有第一次調用才有效果;
      promises.forEach((p, index) => {
        Promise.resolve(p).then(
          value => {
            // 一旦有成功,就将return變為成功;
            resolve(value);
          },
          reason => {
            // 一旦失敗,将return 變為失敗
            reject(reason);
          }
        );
      });
    });
  };

  /**
   * Promise 函數對象的any(promises)方法;
   * 傳回第一個成功狀态的promise;如果都失敗,傳回 AggregateError: All promises were rejected
   */
  Promise.any = function(promises) {
    return new Promise((resolve, reject) => {
      let count = 0; // 記錄失敗的promsie個數
      promises.forEach((p, index) => {
        Promise.resolve(p).then(
          value => {
            resolve(value);
          },
          reason => {
            count++;
            if (count === promises.length) {
              reject("AggregateError: All promises were rejected");
            }
          }
        );
      });
    });
  };

  /**
   * Promsie 函數對象的allSettled(promises)方法;
   * 傳回一個promise,當所有的promise都是已敲定狀态的時候,傳回一個promise,并帶有一個對象數組,
   * 該對象數組是所有settled的promise的資訊;
   * 否則無傳回;
   */
  Promise.allSettled = function(promises) {
    return new Promise((resolve, reject) => {
      const values = new Array(promises.length); // 裝所有settled的promsie
      let count = 0; // 定義已經settled的promsie的個數
      promises.forEach((p, index) => {
        Promise.resolve(p)
          .then(
            value => {
              values[index] = {
                status: "fulfilled",
                value: value
              };
            },
            reason => {
              values[index] = {
                status: "rejected",
                reason: reason
              };
            }
          )
          .then(value => {
            // promise是settled狀态的時候,才會進入這個then
            count++;
            if (count === promises.length) {
              resolve(values);
            }
          });
      });
    });
  };

  // 自定義兩個函數:resolveDelay和rejectDelay;
  /**
   * 傳回一個promise對象,在指定時間過後,才确定promise結果
   */
  Promise.resolveDelay = function(value, time) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (value instanceof Promise) {
          value.then(resolve, reject);
        } else {
          resolve(value);
        }
      }, time);
    });
  };
  // 傳回一個promise對象,在指定時間過後,才失敗
  Promise.rejectDelay = function(reason, time) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        reject(reason);
      }, time);
    });
  };
  // 向外暴露Promise函數
  window.Promise = Promise;
})(window);

           

二、引入剛剛的Promise.js進行測試

test.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Promise</title>
  </head>
  <body>
    <h1>test</h1>
    <!-- 引入自定義的Promise.js檔案 -->
    <script src="./lib/Promise.js"></script>
    <script>
      const p1 = Promise.resolve(1);
      const p2 = Promise.reject(2);
      const pAll = Promise.all([p1, p2, 999]);
      pAll
        .then(value => {
          console.log("value:", value);
        })
        .catch(reason => {
          console.log("reason:", reason);
        });
        // 以上代碼的列印結果:reason: 2
    </script>
  </body>
</html>

           

繼續閱讀