基於promise a+規范手寫promise(promise其實沒那么難!)


一、promise解決了哪些問題?

  1. 異步並發 待所有異步任務結束之后再執行我們的業務邏輯。
  2. 回調嵌套

二、promise特點

1. 每個promise都有三個狀態,pending等待狀態 fulfilled成功態 rejected失敗態
2. 狀態不可逆(若狀態變成了成功態,則會一直維持這個狀態)
3. 每個promise都有一個then方法 傳入兩個參數 一個是成功回調(參數是resolve執行時傳的值) 一個是失敗回調(參數是reject執行時傳的值)
4. new Promise是同步執行的
5. 拋出異常的話 也會變成失敗狀態 走失敗的回調
 

三、簡單示例

    new Promise((resolve,reject) => {
      // 1.狀態從pending改為fulfilled成功態,執行成功的回調
      resolve('success')
      // 2.狀態從pending改為rejected失敗態,執行失敗的回調
      // reject('fail')
      // throw new Error('fail')
    }).then((data) => {
      console.log('success', data);
    }, (err) => {
      console.log('fail', err);
    })

四、手寫實現

0.0.1版

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
  constructor(executor) {
    // 初始狀態為pending等待狀態
    this.status = PENDING;
    const resolve = (value) => {
      // 執行resolve狀態變為成功態
      this.status = FULFILLED;
    };
    const reject = (reason) => {
      // 執行reject狀態變為失敗態
      this.status = REJECTED;
    };
    try {
      // 執行傳入的回調函數
      executor(resolve, reject);
    } catch (e) {
      // 回調函數執行出錯,也會執行reject
      reject(e);
    }
  }
}

由於promise的狀態一旦轉成了成功態或者失敗態,就不能再改變狀態了,所以我們需要加個判斷,只有當狀態為pending等待態的時候,才能將狀態改為成功或失敗,所以當狀態變為了成功,再去調用resolve的話,無法滿足判斷條件,就不會繼續執行了。

0.0.2版

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
  constructor(executor) {
    // 初始狀態為pending等待狀態
    this.status = PENDING;
    const resolve = (value) => {
      // 執行resolve狀態變為成功態
      if (this.status === PENDING) {
        this.status = FULFILLED;
      }
    };
    const reject = (reason) => {
      // 執行reject狀態變為失敗態
      if (this.status === PENDING) {
        this.status = REJECTED;
      }
    };
    try {
      // 執行傳入的回調函數
      executor(resolve, reject);
    } catch (e) {
      // 回調函數執行出錯,也會執行reject
      reject(e);
    }
  }
}

這樣調用的話,狀態改變之后就不會再改了

    let p = new MyPromise((resolve,reject) => {
      resolve('success')
      reject('success')
    })
    console.log('p: ', p);

 

接下來我們實現一下promise實例的then方法,then方法的特點在上面有提到哦

0.0.3版

class MyPromise {
  constructor(executor) {
    // 賦值到this上是為了方便在then方法中調用
    // 初始狀態為pending等待狀態
    this.status = PENDING;
    // 成功回調的參數
    this.value = undefined;
    // 失敗回調的參數
    this.reason = undefined;
    const resolve = (value) => {
      // 執行resolve狀態變為成功態 保存用戶傳入的參數
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
      }
    };
    const reject = (reason) => {
      // 執行reject狀態變為失敗態 保存用戶傳入的參數
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
      }
    };
    try {
      // 執行傳入的回調函數
      executor(resolve, reject);
    } catch (e) {
      // 回調函數執行出錯,也會執行reject
      reject(e);
    }
  }
  // 接收兩個函數作為參數,參數是用戶傳的,傳的第一個回調就代表成功的回調,傳的第二個回調就代表失敗的回調
  then(onFulfilled, onRejected) {
    // 根據當前的狀態,執行對應的回調。回調的參數為用戶調用resolve或者reject傳入的數據
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
  }
}

調用時

    let p = new MyPromise((resolve,reject) => {
      resolve('success')
    })
    p.then((value) => {
      console.log(value);// success
    }, (reason) => {
      console.log(reason);
    })

 但是如果是在異步代碼里面調用resolve方法,就不會執行成功回調了,例如:

    let p = new MyPromise((resolve,reject) => {
      setTimeout(() => {
        resolve('success')
      }, 300);
    })
    p.then((value) => {
      console.log(value);// success
    }, (reason) => {
      console.log(reason);
    })

這是因為代碼執行到settimeout,會將回調放入宏任務隊列,而不是立即執行,所以會先執行then方法,這個時候settimeout並未執行,所以resolve也未執行,此時的status = 'pending',then方法的兩個回調都不會執行。

因此,我們想要執行異步代碼里的resolve或者reject函數的話,需要在執行then方法的時候,先把成功回調或失敗回調先存起來,直到異步代碼執行到resolve,再依次執行回調。實現方式就是采用發布訂閱模式,借助兩個數組,一個是成功回調的數組,一個是失敗回調的數組,在調用then時,若status = pending,就把回調存入對應的數組,之后在resolve(reject)函數中依次調用成功(失敗)回調的數組的回調。

0.0.4版

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
  constructor(executor) {
    // 初始狀態為pending等待狀態
    this.status = PENDING;
    // 成功回調的參數
    this.value = undefined;
    // 失敗回調的參數
    this.reason = undefined;
    this.onFulFilledCallbacks = []; // 存放成功的回調
    this.onRejectedCallbacks = []; // 存放失敗的回調
    const resolve = (value) => {
      // 執行resolve狀態變為成功態 保存用戶傳入的參數
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onFulFilledCallbacks.forEach((fn) => fn()); // 依次執行成功回調隊列的回調
      }
    };
    const reject = (reason) => {
      // 執行reject狀態變為失敗態 保存用戶傳入的參數
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn()); // 依次執行失敗回調隊列的回調
      }
    };
    try {
      // 執行傳入的回調函數
      executor(resolve, reject);
    } catch (e) {
      // 回調函數執行出錯,也會執行reject
      reject(e);
    }
  }
  // 接收兩個函數作為參數,參數是用戶傳的,傳的第一個回調就代表成功的回調,傳的第二個回調就代表失敗的回調
  then(onFulfilled, onRejected) {
    // 根據當前的狀態,執行對應的回調。回調的參數為用戶調用resolve或者reject傳入的數據
    if (this.status === FULFILLED) {
      onFulfilled(this.value);
    }
    if (this.status === REJECTED) {
      onRejected(this.reason);
    }
    // 狀態為pending時 將用戶傳的回調存放到各自的隊列中(若用戶沒有調用resolve或reject,則不會執行隊列中的回調)
    if (this.status === PENDING) {
      this.onFulFilledCallbacks.push(() => {
        onFulfilled(this.value);
      });
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason);
      });
    }
  }
}

調用時

    let p = new MyPromise((resolve,reject) => {
      setTimeout(() => {
        resolve('success')
      }, 300);
    })
    p.then((value) => {
      console.log(value, 1);// success 1
    }, (reason) => {
      console.log(reason);
    })
    p.then((value) => {
      console.log(value, 2);// success 2
    }, (reason) => {
      console.log(reason);
    })

接下來我們來分析then鏈式調用的規則

(1)如果then方法中成功回調或失敗回調返回的是一個非promise值,則將這個值傳遞給外層下一次then的成功回調參數

(2)如果then方法中成功回調或失敗回調的執行報錯了,則將錯誤信息傳遞給外層下一次then的失敗回調參數

    let pp = new Promise((resolve,reject) => {
      resolve(1)
    }).then((data) => {
      console.log('第一次 success', data);//第一次 success 1
      return 100;
      // throw new Error('error')
    }, (err) => {
      console.log('第一次 fail', err);
    }).then((data) => {
      console.log('第二次 success', data);//第二次 success 100
    }, (err) => {
      console.log('第二次 fail', err);
    })

(3)如果then方法中成功回調返回的是一個promise值,

  • 若在返回的這個promise內部調用了resolve函數,則將傳入resolve的參數 傳遞給外層下一次then的成功回調參數;
  • 若在返回的這個promise內部調用了reject函數,則將傳入reject的參數,傳遞給外層下一次then的失敗回調參數;
    let pp2 = new Promise((resolve,reject) => {
      resolve(1)
    }).then((data) => {
      console.log('第一次 success', data);//第一次 success 1
      return new Promise((resolve, reject) => {
        resolve(100);
        // reject(200)
      });
    }, (err) => {
      console.log('第一次 fail', err);
    }).then((data) => {
      console.log('第二次 success', data);//第二次 success 100
    }, (err) => {
      console.log('第二次 fail', err);
    })

(4)catch方法相當於then(null, err => {})

    let pp2 = new Promise((resolve,reject) => {
      resolve(1)
    }).then((data) => {
      console.log('第一次 success', data);//第一次 success 1
      return new Promise((resolve, reject) => {
        // resolve(100);
        reject(200);
      });
    }, (err) => {
      console.log('第一次 fail', err);
    }).catch((data) => {
      console.log('第二次 fail', data);//第二次 fail 200
    });

then的鏈式調用是如何實現的?

每次調用then,返回一個新的promise實例,這個實例上肯定也有then方法,就可以一直.then下去

0.0.5版

class MyPromise {
  constructor(executor) {
    // 省略了跟上一版一樣的內容
  }
  then(onFulfilled, onRejected) {
    // 調用then的時候 會創建一個新的promise實例並返回
    let promise2 = new MyPromise((resolve, reject) => {
      // 這里面的resolve和reject是promise2的 當在promise2里面調用resolve,就會執行promise2.then里面的成功回調
      if (this.status === FULFILLED) {
        // 需要拿到成功回調的返回值,傳遞給下一個then
        let x = onFulfilled(this.value);
        resolve(x);
      }
      if (this.status === REJECTED) {
        let x = onRejected(this.reason);
        resolve(x);
      }
      if (this.status === PENDING) {
        this.onFulFilledCallbacks.push(() => {
          let x = onFulfilled(this.value);
          resolve(x);
        });
        this.onRejectedCallbacks.push(() => {
          let x = onRejected(this.reason);
          resolve(x);
        });
      }
    });
    return promise2;
  }
}

這里涉及到MyPromise函數的遞歸執行,在調用函數時進行拆分,就好分析了

    let pp = new Promise((resolve,reject) => {
      // resolve(1)
      reject(1)
    })
    // 調用pp的then會返回promise2
    let promise2 = pp.then(data => {
      // 執行pp的成功回調 返回普通值x 會觸發promise2的成功回調 相當於在這個promise2的內部調用resolve(x)
      // return 100;
    }, err => {
      // 執行pp的失敗回調 也會觸發promise2的成功回調
      return 100;
    })
    promise2.then((data) => {
      console.log(data);// 100
    }, (err) => {
      console.log(err);
    })

 上個版本只是實現了規則(1),處理的是成功或失敗回調返回非promise值的情況,下面處理一下成功或失敗的回調在執行時發生報錯的情況,只需要加上try catch即可

0.0.6版

class MyPromise {
  constructor(executor) {
    // 此處省略
  }
  then(onFulfilled, onRejected) {
    // 調用then的時候 會創建一個新的promise實例並返回
    let promise2 = new MyPromise((resolve, reject) => {
      // 這里面的resolve和reject是promise2的 當在promise2里面調用resolve,就會執行promise2.then里面的成功回調
      if (this.status === FULFILLED) {
        // 需要拿到成功回調的返回值,傳遞給下一個then
        try {
          let x = onFulfilled(this.value);
          resolve(x);
        } catch (e) {
          reject(e);
        }
      }
      if (this.status === REJECTED) {
        try {
          let x = onRejected(this.reason);
          resolve(x);
        } catch (e) {
          reject(e);
        }
      }
      if (this.status === PENDING) {
        this.onFulFilledCallbacks.push(() => {
          try {
            let x = onFulfilled(this.value);
            resolve(x);
          } catch (e) {
            reject(e);
          }
        });
        this.onRejectedCallbacks.push(() => {
          try {
            let x = onRejected(this.reason);
            resolve(x);
          } catch (e) {
            reject(e);
          }
        });
      }
    });
    return promise2;
  }
}

接下來處理規則(3),也就是then方法中返回一個promise值的情況,需要給返回值注冊成功和失敗的回調,成功回調中執行promise2的resolve,失敗回調中執行promise2的reject

規則(4)實際就是調用了then方法

0.0.7版(最終版)

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function resolvePromise(x, promise2, resolve, reject) {
  // then中成功或失敗回調的返回值 不能和調用then方法返回的promise值相等
  if (x === promise2) {
    return reject(new TypeError('出錯'));
  }
  if (x instanceof MyPromise) {
    try {
      let then = x.then; 
      then.call(
        x,
        (y) => {
          // y代表返回的promise值內部調用resolve時傳的參數 傳給promise2的resolve方法並執行
          resolve(y);
        },
        (r) => {
          // r代表返回的promise值內部調用reject時傳的參數 傳給promise2的reject方法並執行
          reject(r);
        },
      );
    } catch (e) {
      reject(e);
    }
  } else {
    // 非promise值
    resolve(x);
  }
}
class MyPromise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    this.onFulFilledCallbacks = []; // 存放成功的回調
    this.onRejectedCallbacks = []; // 存放失敗的回調
    const resolve = (value) => {
      if (this.status === PENDING) {
        this.status = FULFILLED;
        this.value = value;
        this.onFulFilledCallbacks.forEach((fn) => fn()); // 依次執行成功回調隊列的回調
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn()); // 依次執行失敗回調隊列的回調
      }
    };
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }
  then(onFulfilled, onRejected) {
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 使用settimeout是為了異步執行處理返回值的代碼 否則直接使用promise2會報錯
        setTimeout(() => {
          // 需要拿到成功回調的返回值,傳遞給下一個then
          try {
            let x = onFulfilled(this.value);
            // 統一處理返回值
            resolvePromise(x, promise2, resolve, reject);
          } catch (e) {
            console.log(e);
          }
        }, 0);
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(x, promise2, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }
      if (this.status === PENDING) {
        this.onFulFilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(x, promise2, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(x, promise2, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return promise2;
  }
    catch(onRejected) {
      return this.then(null, onRejected);
    }
}

 調用時

    let pp = new MyPromise((resolve,reject) => {
      resolve(1)
    })
    // 調用pp的then會返回promise2
    let promise2 = pp.then(data => {
      // 執行pp的成功回調 返回promise值x 會給x注冊成功和失敗回調,
      // 成功回調中執行promise2的成功回調,失敗回調中執行promise2的失敗回調
      return new MyPromise((res, rej) => {
        res(3000)
      })
    })
    // 若執行promise類型的返回值的resolve,就會執行第一個回調
    promise2.then((data) => {
      console.log(data);// 3000
    }, (err) => {
      console.log(err);
    })

 

 好啦!終於整理完成了!看完之后是不是發現promise其實也沒那么難了!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM