學習Promise && 簡易實現Promise

Promise和異步

  • Promise異步解決方案,是其餘異步方案的基石
  • 異步:如今和未來之間的時間間隙
  • Promises/A+規範

Promise核心方法和輔助方法

  • Promise.prototype.then ( 核心 )
  • Promise.prototype.catch ( 輔助 )
  • Promise.resolve ( 輔助 )
  • Promise.reject ( 輔助 )
  • Promise.all ( 輔助 )
  • Promise.race ( 輔助 )
  • 阮一峯(promise)

Promise

  1. promise 狀態變動後不可逆
  2. 同一個實例能夠被屢次 then
  3. .then方法是異步且能夠被鏈式調用( 返回新的promise )
  4. .then方法中用戶能夠顯示的返回 promise ,且能夠多層嵌套。實例以下圖

http://occeqxmsk.bkt.clouddn.com/status_-all.png

Promise中也許是最麻煩的部分1 ( 訂閱發佈 )

  1. new Promise 中使用了異步方法,那麼當前狀態就會一直處於 pending 狀態,直到在異步的回調中觸發 resolvereject
  2. 那麼在構造函數 Promise 中,就須要把then裏面的成功和失敗函數,存儲在私有數組中 this.onResolvedArythis.onRejectedAry
  3. then 方法爲 pending 狀態時,把第一個成功參數 onFulFilled 和第二個失敗參數 onRejected,分別存儲到私有變量 this.onResolvedArythis.onRejectedAry
  4. 當異步回調中的 resolverejcet 被觸發時,分別遍歷對應的數組 (onResolvdeAry, onRejectedAry) 並執行便可

Promise中也許是最麻煩的部分2 ( 遞歸 )

默認返回Promise和用戶顯示的返回Promisejavascript

  1. defaultReturnPromise.then 中默認返回新的 promise (鏈式調用)
  2. userReturnValue:用戶能夠顯示的返回 promise 或其餘參數
  3. 若是用戶返回的是普通字符,則直接調用 defaultReturnPromiseresolve
  4. 若是用戶返回的是一個 promise,則看用戶返回的 promise 內部調用的 resolve 仍是 reject
  5. 若是調用的是 resolve ,則拿着 resolve 的參數直接執行 defaultReturnPromiseresolve
  6. 若是調用的是 reject,則拿着這個錯誤參數直接執行 defaultReturnPromisereject
  7. 若是 resolve 中執行的又是一個新的 promise ,則重複執行上述規則(2-6)

new Promise輪廓

class Promise {
  constructor(executor) {
    let resolve = success_value => {};
    let reject = fail_reason => {};
    executor(resolve, reject);
  }
  then(onFulFilled, onRejected) {}
}
複製代碼

狀態不可逆

Promise/A+ (2.1) promise狀態: 必須處於如下三種狀態中的一種 pending( 等待 )、fulfilled( 完成 )、rejected( 失敗 )java

pending 能夠轉換成 fulfilledrejected,只要轉換了完成或失敗,則不可逆es6

class Promise {
  constructor(executor) {
    this.status = 'pending';// 默認狀態
    this.success_data;// 成功傳遞的數據
    this.fail_reason;// 失敗傳遞的數據
    // 執行成功
    let resolve = success_data => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';// 變動爲成功狀態
        this.success_data = success_data;// 把內容傳遞進success_data中
      }
    };
    // 執行失敗
    let reject = fail_reason => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.fail_reason = fail_reason;
      }
    };
    // new Promise的第一個參數會當即執行
    executor(resolve, reject);
  }
  then(onFulFilled, onRejected) {
    if (this.status === 'fulfilled') {
      onFulFilled(this.success_data);
    }
    if (this.status === 'rejected') {
      onRejected(this.fail_reason);
    }
  }
}
複製代碼

同一個實例能夠被屢次then

class Promise {
  constructor(executor) {
    this.status = 'pending';// 默認狀態
    this.success_data;// 成功傳遞的數據
    this.fail_reason;// 失敗傳遞的數據
    this.onResolvedAry = [];// 存放成功的回調
    this.onRejectedAry = [];// 存放失敗的回調
    // 執行成功
    let resolve = success_data => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.success_data = success_data;// 把內容傳遞進來
        this.onResolvedAry.forEach(item => item());// 遍歷緩存列表,依次觸發裏面的回調函數
      }
    };
    // 執行失敗
    let reject = fail_reason => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.fail_reason = fail_reason;
        this.onRejectedAry.forEach(item => item());
      }
    };
    // new Promise的第一個參數會當即執行
    executor(resolve, reject);
  }
  then(onFulFilled, onRejected) {
    if (this.status === 'fulfilled') {
      onFulFilled(this.success_data);
    }
    if (this.status === 'rejected') {
      onRejected(this.fail_reason);
    }
    // 異步執行時,status狀態處於pending狀態
    if (this.status === 'pending') {
      // 同一個實例能夠被屢次then,在異步成功後調用resolve或者reject方法,以分別執行onResolvedAry,onRejectedAry兩個數組
      // 添加到緩存列表、以供異步成功後執行的resolve或reject所循環調用
      this.onResolvedAry.push(onFulFilled(this.success_data));
      this.onRejectedAry.push(onRejected(this.fail_reason));
    }
  }
}
複製代碼

.then方法是異步的

// 須要在then的狀態判斷下添加setTimeout(() => {}, 0)來模擬異步效果
複製代碼

.then方法默認返回新的Promise(鏈式調用)

.then 方法中,狀態判斷下添加 - 返回新的 new Promise( 鏈式調用 ),在 promise 添加 setTimeout 以模擬異步執行數組

// 須要在then的狀態判斷下添加並返回一個新的Promise
class Promise {
  constructor(executor) {
    this.status = 'pending';// 默認狀態
    this.success_data;// 成功傳遞的數據
    this.fail_reason;// 失敗傳遞的數據
    this.onResolvedAry = [];// 存放成功的回調
    this.onRejectedAry = [];// 存放失敗的回調
    // 執行成功
    let resolve = success_data => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';
        this.success_data = success_data;// 把內容傳遞進來
        this.onResolvedAry.forEach(item => item());
      }
    };
    // 執行失敗
    let reject = fail_reason => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.fail_reason = fail_reason;
        this.onRejectedAry.forEach(item => item());
      }
    };
    executor(resolve, reject);
  }
  then(onFulFilled, onRejected) {
    let promise2;
    if (this.status === 'fulfilled') {
      promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          onFulFilled(this.success_data);
        }, 0);
      });
    }
    if (this.status === 'rejected') {
      promise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
          onRejected(this.fail_reason);
        }, 0);
      });
    }
    // 異步執行時,status處於pending狀態
    // new Promise的第一個參數會當即執行,
    if (this.status === 'pending') {
      // 同一個實例能夠被屢次then,在異步成功後調用resolve或者reject方法,以分別執行onResolvedAry,onRejectedAry兩個數組
      promise2 = new Promise((resolve, reject) => {
        setTimeou(() => {
          this.onResolvedAry.push(onFulFilled(this.success_data));
          this.onRejectedAry.push(onRejected(this.fail_reason));
        }, 0);
      });
    }
  }
}
複製代碼

.then方法中用戶能夠顯示的返回Promise,並能夠多層嵌套

截取其中的一段代碼promise

then(onFulFilled, onRejected) {
  let promise2;
  if (this.status === 'fulfilled') {
    promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        let x = onFulFilled(this.success_data);
        resolvePromise(promise2, x, resovle, reject);
        // x爲用戶輸入的返回值,p.then(data => { return "123" })
        // 若是是非promise的話,此時能夠直接調用新的promise2的resolve方法
        // 若是是promise,那麼就須要在then中拿到異步回來的參數,以調用promise2的resolve方法
        // 由於resolve或者reject中能夠嵌套promise因此,因此咱們須要把這個判斷抽離出一個函數,以方便遞歸調用本身
      }, 0);
    });
  }
}

resolvePromise(promise2, x, resolve, reject) {
  // typeof null === object
  if(x !== null && (typeof x === 'object' || typeof x === 'function')) {
    let then = x.then;
    // 判斷then是不是函數,也許是一個對象
    if (typeof then === 'function') {
      then.call(x, (data) => {
        // 若是resolve中仍是一個promise,須要遞歸處理,直到出現不是promise值爲止,而後調用promise2的resolve或reject。以返回到主流程的下一個then中
        resolvePromise(promise2, data, resolve, reject);
      }, (error) => {
        reject(error);
      });
    } else {
      // 若是不是一個函數,多是一個JSON
      resolve(x);
    }
  } else {
    // 若是不是promise就直接調用promise2的resolve
    resolve(x)
  }
}
複製代碼

.then的代碼

// 解析默認返回的promise和用戶輸入的promise之間的關係
function resolvePromise(promise2, x, resolve, reject) {
  // 這裏面的resolve和reject都是promise2的
  // 判斷x是不是Promise(核心)
  if (promise2 === x) {
    // 2.3.1 若是promise和x指向同一個對象,爆出TypeError類型錯誤,表示拒絕
    return reject(new TypeError('引用錯誤'));
  }
  // 2.3.2 若是x是一個對象或者函數,再去取then方法
  // typeof null === object
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    // 若是去檢索x.then方法,拋出了異常,那麼久應該reject異常拋出
    let onOff;// 防止成功以後再調取失敗。。。爲了兼容別人的Proimse
    try {// 防止取then時,報錯
      let then = x.then;// 2.3.3.1 取x的then方法
      // 2.3.3.3 若是then是一個函數,就認爲其實Promise
      if (typeof then === 'function') {
        // 讓x.then執行。用戶輸入的promise執行then方法
        then.call(x, (data) => {
          if (onOff) return;
          onOff = true;
          // data有可能仍是一個Promise
          // 遞歸解析Promise
          resolvePromise(promise2, data, resolve, reject);
        }, (error) => {
          if (onOff) return;
          onOff = true;
          reject(error);
        });
      } else {
        resolve(x);
      }
    } catch (error) {
      if (onOff) return;
      onOff = true;
      reject(error);
    }
  } else {// 2.3.4 若是不是object或function 則直接fulfill 成功
    // 若是是一個普通值,則直接調用resolve把普通值傳遞進去
    resolve(x);
  }
}

class Promise {
  constructor(executor) {
    this.status = 'pending';
    this.success_value;
    this.fail_reason;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];
    let resolve = data => {
      if (this.status === 'pending') {
        this.status = 'fulfilled';// 變動狀態
        this.success_value = data;// 傳遞值
        this.onResolvedCallbacks.forEach(item => item());
      }
    }
    let reject = reason => {
      if (this.status === 'pending') {
        this.status = 'rejected';
        this.fail_reason = reason;
        this.onRejectedCallbacks.forEach(item => item());
      }
    }
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }
  then(onFulFilled, onRejected) {
    let promise2;
    // 添加鏈式操做
    if (this.status === 'fulfilled') {
      // 執行完成以後,返回一個新的Promise,以方便下一次鏈式調用
      promise2 = new Promise((resolve, reject) => {
        // 查看onFulFilled執行完的結果是什麼?
        let x = onFulFilled(this.success_value);
        // 若是x返回的是一個普通值,則把這個普通只做爲promise2成功的結果
        // 若是x是Promise,則取其結果,做爲promise2成功的結果
        // 如今須要判斷x和promise2的關係
        // promise2是then中執行完畢後的回調Proimse
        // x是 then中用戶輸入寫的return值
        // 若是用戶return的是一個普通值:那麼須要傳遞給Promise2()的成功結果
        // 若是用戶return的是一個Promise:那麼須要斷定是成功仍是失敗

        // 解析promise,去x的結果,而後讓promise2成功或者失敗
        resolvePromise(promise2, x, resolve, reject);
      });
    }
    if (this.status === 'rejected') {
      promise2 = new Promise((resolve, reject) => {
        let x = onRejected(this.fail_reason);
        resolvePromise(promise2, x, resolve, reject);
      });
    }
    if (this.status === 'pending') {
      promise2 = new Promise((resolve, reject) => {
        // 存儲值,在異步成功以後循環當前數組執行
        this.onResolvedCallbacks.push(() => {
          let x = onFulFilled(this.success_value);
          resolvePromise(promise2, x, resolve, reject);
        });
        this.onRejectedCallbacks.push(() => {
          let x = onRejected(this.fail_reason);
          resolvePromise(promise2, x, resolve, reject);
        });
      });
    }
    return promise2;
  }
}
複製代碼

.catch方法

// catch就是then的第二個錯誤的簡寫
catch(onRejected) {
  return this.then(null, onRejected);
}
複製代碼

Promise.resolve

Promise 上的靜態方法 resolvereject ,直接調用靜態方法中返回的新的 Promise 中的resolvereject便可緩存

Promise.resolve = function (value) {
  return new Promise((resolve, reject) => resolve(value));
}
Promise.reject = function (value) {
  return new Promise((resolve, reject) => reject(value));
}
複製代碼

Promise.all([p1, p2, p3])

  1. 數組中全部的異步數據回來後,才執行 then 中成功回調
  2. 循環數組的 then 方法,若是 then 的數值 all 回來了,則調用新的 Promiseresolve,若是沒有所有回來,則調 reject
  3. 若是數組中的異步直接調取了 reject 的,則直接調用新的 Promisereject
Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let arr = [];
    let idx = 0;// 爲了保證獲取所有成功,來設置的索引.不能使用arr.length來判斷,若是數組最後的異步先回來,那麼此時arr和promises的長度就相等了 - 亂套了
    function processData(index, data) {
      // 和all的數組一一對應,arr數組和promises長度相等時成功
      arr[index] = data;
      // 給第二個複製,第一個的數值就是空的undefined,萬一數組中的最後一個回來他們就都成功了 arr.length === promises.lenght
      idx++;
      if (idx === promises.length) {
        resolve(arr);
      }
    }
    for (let i = 0; i < promises.length; i++) {
      promises[i].then((data) => {
        // all 要根據數組的順序來存放
        processData(i, data);
      }, reject);
    }
  });
}
複製代碼

Promise.race

第一個參數中只要有一個異步數據回來,就執行異步

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      // 只要有一個異步數據回來,誰回來的早就搞誰
      promises[i].then(resolve, reject);
    })
  });
}
複製代碼

附:這篇博客 也許 想表達 promise 其實相對而言比較簡單 (⊙﹏⊙)b函數

相關文章
相關標籤/搜索