手寫Promise

大豆F4發表於2024-08-17

  實現功能如下:

  1. Promise建構函式
  2. promise例項then方法
  3. pomise例項catch方法
  4. promise例項finally方法
  5. Promise靜態resolve方法
  6. Promise靜態reject方法
  7. Promise靜態all方法
    備註:參照渡一教育影片
    程式碼:
      1 const Pending = 'pending';
      2 const FullFiled = 'fulfilled';
      3 const Rejected = 'rejected';
      4 class MyPromise {
      5     #status = Pending;
      6     #value = '';
      7     #taskList = [];
      8     constructor(executor) {
      9         try {
     10             executor(this.#resolve, this.#reject);
     11         } catch (error) {
     12             this.#reject(error);
     13         }
     14 
     15     }
     16     #resolve = (data) => {
     17         this.#changeState(FullFiled, data);
     18     }
     19 
     20     #reject = (reason) => {
     21         this.#changeState(Rejected, reason);
     22     }
     23 
     24     #changeState = (status, value) => {
     25         if (status === Pending) return;
     26         this.#status = status;
     27         this.#value = value;
     28         this.#eventLoop();
     29     }
     30 
     31     // 判斷then中的返回值是否複合PromiseA+規範
     32     #isPromiseA = (callBack) => {
     33         if (callBack !== null && (typeof callBack === 'object' || typeof callBack === 'array')) {
     34             return typeof callBack.then === 'function';
     35         }
     36         return false;
     37     }
     38 
     39     // 手動將promise.then中的函式放入微佇列中
     40     // nodeJS環境使用process.nextTick()
     41     // 瀏覽器環境使用MutationObserver()
     42     #addMicroTaskList = (callBack) => {
     43         if (typeof process === 'object' && typeof process.nextTick === 'function') {
     44             process.nextTick(callBack);
     45             return;
     46         }
     47         if (typeof MutationObserver === 'function') {
     48             let m = new MutationObserver(callBack);
     49             let div = document.createElement("div");
     50             m.observe(div, {
     51                 childList: true
     52             });
     53             div.innerText = "1";
     54             return;
     55         }
     56         // 其他環境
     57         setTimeout(callBack, 0);
     58     }
     59 
     60     // promise.then中的函式需要放到微佇列執行,
     61     // then中的引數有三種情況
     62     // 1. 引數不是函式,promise穿透到下一個then
     63     // 2. 引數是一個函式,
     64     // 2.1 函式的返回值不是promise,直接resolve(this.value)
     65     // 2.2 函式的返回值是promise, 呼叫promise.then()
     66     #handelOneTask = (callBack, resolve, reject) => {
     67         this.#addMicroTaskList(() => {
     68             if (typeof callBack === 'function') {
     69                 try {
     70                     const result = callBack(this.#value);
     71                     if (this.#isPromiseA(result)) {
     72                         result.then(resolve, reject);
     73                     } else {
     74                         resolve(result);
     75                     }
     76                 } catch (error) {
     77                     reject(error);
     78                 }
     79 
     80             } else {
     81                 // 如果then中的兩個引數不是函式,則promise穿透到下一層then
     82                 if (this.#status === FullFiled) {
     83                     resolve(this.#value);
     84                 } else if (this.#status === Rejected) {
     85                     reject(this.#value);
     86                 }
     87             }
     88         });
     89     }
     90 
     91     // 每次呼叫then方法和promise的狀態發生改變時,執行then中的回撥函式
     92     #eventLoop = () => {
     93         if (this.#status === Pending) return;
     94         while (this.#taskList.length) {
     95             const { successCal, errorCal, resolve, reject } = this.#taskList.shift();
     96             switch (this.#status) {
     97                 case FullFiled:
     98                     this.#handelOneTask(successCal, resolve, reject);
     99                     break;
    100                 case Rejected:
    101                     this.#handelOneTask(errorCal, resolve, reject);
    102                     break;
    103                 default:
    104                     break;
    105             }
    106 
    107         }
    108     }
    109 
    110     // then方法接收resolve和reject的回撥函式
    111     then = (successCal, errorCal) => {
    112         return new MyPromise((resolve, reject) => {
    113             this.#taskList.push({ successCal, errorCal, resolve, reject });
    114             this.#eventLoop();
    115         })
    116     }
    117 
    118     catch = (callBack) => {
    119         return this.then(undefined, callBack);
    120     }
    121 
    122     finally = (callBack) => {
    123         return this.then((data) => {
    124             callBack();
    125             return data;
    126         }, (err) => {
    127             callBack();
    128             throw err;
    129         })
    130     }
    131 
    132     static resolve = (value) => {
    133         if (value instanceof MyPromise) return value;
    134         let _resolve, _reject;
    135         const p = new MyPromise((resolve, reject) => {
    136             _resolve = resolve;
    137             _reject = reject;
    138         });
    139         if (p.#isPromiseA(value)) {
    140             value.then(_resolve, _reject)
    141         } else {
    142             _resolve(value);
    143         }
    144         return p;
    145     }
    146 
    147     static reject = (reason) => {
    148         return new MyPromise((resolve, reject) => {
    149             reject(reason);
    150         })
    151     }
    152 
    153     static all = (iterable) => {
    154         let _resolve, _reject;
    155         const p = new MyPromise((res, rej) => {
    156             _resolve = res;
    157             _reject = rej;
    158         });
    159         let successCount = 0;
    160         let count = 0;
    161         let data = [];
    162         for (const item of iterable) {
    163             const i = count;
    164             count++;
    165             MyPromise.resolve(item).then(res=>{
    166                 successCount++;
    167                 data[i] = res;
    168                 if(count === successCount) {
    169                     _resolve(data);
    170                 }
    171             }, _reject )
    172         };
    173         if (count === 0) {
    174             _resolve(data);
    175         }
    176         return p;
    177     }
    178 }
    179 
    180 const a = new MyPromise((resolve, reject) => {
    181     // resolve(123)
    182     // reject('error')
    183     // setTimeout(() => {
    184     //     resolve(123);
    185     // }, 1000);
    186     // throw 'error'
    187 });

相關文章