【前端 JavaScript 高級】06 - Promise的入門及自定義Promise

第 1 章 Promise的理解和使用

1.1 Promise 是什么?

1.1.1 理解
抽象表達(dá)

Promise 是一門新的技術(shù)(ES6規(guī)范)拥诡;
PromiseJS 中進(jìn)行異步編程的新解決方案,備注:舊方案是單純使用回調(diào)函數(shù).
異步編程: fs文件操作、數(shù)據(jù)庫操作鸠补、ajax刻像、定時(shí)器铡恕。

具體表達(dá)

從語法上來說: Promise 是一個(gè)構(gòu)造函數(shù)铁材;
從功能上來說: Promise 對象用來封裝一個(gè)異步操作并可以獲取其成功/失敗的結(jié)果值问慎。

1.1.2 promise 的狀態(tài)改變
  1. pending 變?yōu)?resolved;

  2. pending 變?yōu)?rejected;

  3. Promise初始狀態(tài)是pending型雳。

說明: 只有這 2 種, 且一個(gè) promise 對象只能改變一次,無論變?yōu)槌晒€是失敗, 都會有一個(gè)結(jié)果數(shù)據(jù)成功的結(jié)果數(shù)據(jù)一般稱為 value, 失敗的結(jié)果數(shù)據(jù)一般稱為 reason当凡。

Promise對象的兩個(gè)屬性
PromiseState : Promise 的狀態(tài) 是實(shí)例對象中的一個(gè)屬性 可能的值有:
  1. pending(未決定的)山害;

  2. resolvedfullfilled 成功的);

  3. rejectrejected失敗的)沿量;

狀態(tài)的改變只會從 pending => fullfilled 或者 pending => rejected浪慌;

PromiseResult 是實(shí)例對象中的另一個(gè)屬性 保存著對象【成功/失敗】的結(jié)果 保存異步任務(wù)成功或者失敗的結(jié)果
  1. resolve

  2. reject朴则;

1.1.3 Promise初體驗(yàn) : 抽獎(jiǎng)案例
  1. 頁面上有一個(gè)按鈕當(dāng)點(diǎn)擊按鈕之后1秒 权纤,顯示是否中獎(jiǎng),不管中沒中獎(jiǎng)都給出相應(yīng)的提示信息乌妒。
/**
   * 生成隨機(jī)數(shù)
   * */
  function rand(m, n) {
    return Math.ceil(Math.random() * (n - m + 1)) + m - 1;
  }

  /**
   * 1. 點(diǎn)擊按鈕汹想,1s后顯示是否 中獎(jiǎng)(30%概率中獎(jiǎng))
   *    1.1 如果中獎(jiǎng)彈出恭喜恭喜,獎(jiǎng)品...
   *    1.2 如果未中獎(jiǎng)彈出撤蚊,再接再厲
   */

    // 1. 傳統(tǒng)回調(diào)函數(shù)的方式實(shí)現(xiàn)
  let btn = document.querySelector('#btn');
  /*btn.addEventListener('click', () => {
    setTimeout(function () {
      let n = rand(1, 100);// 獲取 1~100的隨機(jī)數(shù)
      if (n <= 30) {
        alert('恭喜恭喜古掏,您中獎(jiǎng)了!');
      } else {
        alert('很遺憾,請?jiān)賮硪淮?');
      }
    }, 1000);
  });*/


  // 2. Promise 形式實(shí)現(xiàn)
  /**
   * resolve 解決 函數(shù)類型的數(shù)據(jù)
   *
   * reject 拒絕 函數(shù)類型的數(shù)據(jù)
   */
  btn.addEventListener('click', () => {
    let p = new Promise((resolve, reject) => {
      setTimeout(function () {
        let n = rand(1, 100);// 獲取 1~100的隨機(jī)數(shù)
        if (n <= 30) {
          resolve(n);
        } else {
          reject(n);
        }
      }, 1000);
    });

    /**
     * 1. 需求侦啸,將中獎(jiǎng)的數(shù)字顯示出來
     */
    p.then(value => {
        alert('恭喜恭喜槽唾,您中獎(jiǎng)了! 將號碼是 => ' + value);
      },
      reason => {
        alert('很遺憾,請?jiān)賮硪淮?');
      })

  });
1.1.4 promise 的基本流程
Promise的基本執(zhí)行流程

1.2 為什么要用 Promise?

1.2.1 指定回調(diào)函數(shù)的方式更加靈活
  1. 舊的: 必須在啟動(dòng)異步任務(wù)前指定匹中。

  2. promise: 啟動(dòng)異步任務(wù) => 返回 promie對象 => 給 promise對象綁定回調(diào)函數(shù)(甚至可以在異步任務(wù)結(jié)束后指定/多個(gè))夏漱。

1.2.2 支持鏈?zhǔn)秸{(diào)用, 可以解決回調(diào)地獄問題
1. 什么是回調(diào)地獄?

答:回調(diào)函數(shù)嵌套調(diào)用, 外部回調(diào)函數(shù)異步執(zhí)行的結(jié)果是嵌套的回調(diào)執(zhí)行的條件。

2. 回調(diào)地獄的缺點(diǎn)?

不便于閱讀
不便于異常處理

3. 解決方案?

promise 鏈?zhǔn)秸{(diào)用

4. 終極解決方案?

async/await

1.2.3 Promise封裝 Ajax請求顶捷,點(diǎn)擊按鈕發(fā)送請求
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"
        ,maximum-scale=1.0,minimum-scale=1.0>
  <title>Promise封裝ajax請求</title>
  <link rel="stylesheet" href="../bootstrap/css/bootstrap.min.css">
</head>
<body>

<div class="container">
  <div class="page-header">Promise封裝ajax請求</div>
  <button class="btn btn-primary" id="btn">發(fā)送ajax請求</button>
</div>

<script src="../js/jquery.min.js"></script>
<script src="../bootstrap/js/bootstrap.min.js"></script>
<script>

  let btn = document.querySelector('#btn');

  btn.addEventListener('click', function () {
    // https://api.apiopen.top/getJoke
    let p = new Promise((resolve, reject) => {
      // 1. 創(chuàng)建對象
      const xhr = new XMLHttpRequest();
      // 2. 初始化
      xhr.open('GET', 'https://api.apiopen.top/getJoke');

      // 3. 發(fā)送
      xhr.send();
      // 4. 處理響應(yīng)結(jié)果
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          // 判斷響應(yīng)狀態(tài)碼是否在 2xx
          if (xhr.status >= 200 && xhr.status) {
            resolve(xhr.response);
          } else {
            reject(xhr.status);
          }
        }
      }
    });

    p.then(value => {
        console.log(value);
      },
      reason => {
        console.warn(reason);
      });
  });

</script>

</body>
</html>
1.2.4 Promise封裝ajax請求挂绰,傳遞參數(shù)返回一個(gè)Promise對象
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"
        ,maximum-scale=1.0,minimum-scale=1.0>
  <title>Promise封裝ajax請求</title>
</head>
<body>

<script>

  /**
   * 封裝一個(gè)函數(shù) sendAjax方法GET AJAX 請求
   * 參數(shù) URL
   * 返回結(jié)果 Promise 對象
   */ 
  function sendAjax(url) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.responseType = 'json';
      xhr.open('GET', url);
      xhr.send();

      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300) {
            resolve(xhr.response);
          } else {
            reject(xhr.status);
          }
        }
      }
    });
  }

  sendAjax('https://api.apiopen.top/getJoke').then(
    value => {
      console.log(value);
    },
    reason => {
      console.log(reason);
    }
  );
</script>
</body>
</html>

1.3 如何使用Promise

1.3.1 API
Promise 構(gòu)造函數(shù): Promise (excutor) {};
  1. executor 函數(shù): 執(zhí)行器 (resolve, reject) => {}服赎;

  2. resolve 函數(shù): 內(nèi)部定義成功時(shí)我們調(diào)用的函數(shù) value => {}葵蒂;

  3. reject 函數(shù): 內(nèi)部定義失敗時(shí)我們調(diào)用的函數(shù)reason => {}說明: executor 會在 Promise 內(nèi)部立即同步調(diào)用,異步操作在執(zhí)行器中執(zhí)行。

/**
   * Promise 構(gòu)造函數(shù)的 參數(shù)就是一個(gè)執(zhí)行器重虑,執(zhí)行器函數(shù)是在Promise中同步調(diào)用的
   * @type {Promise<any>}
   */
  let p = new Promise((resolve, reject) => {
    // 此函數(shù)內(nèi)部的代碼是同步執(zhí)行的
    console.log(11); // 第一打印
    // 修改Promise對象的狀態(tài)
    reject('error');
  });

  console.log(22); // 第二打印

  /**
   *  都是函數(shù)類型的參數(shù)
   */
  p.then(value => {

    },
    reason => {

    });

  /**
   * catch只能指定失敗的回調(diào)
   */
  p.catch(reason => {
    console.log(reason);
  });
Promise.prototype.then 方法: (onResolved, onRejected) => {}
  1. onResolved 函數(shù): 成功的回調(diào)函數(shù)(value) => {};

  2. onRejected 函數(shù): 失敗的回調(diào)函數(shù) (reason) => {};

說明: 指定用于得到成功 value 的成功回調(diào)和用于得到失敗 reason 的失敗回調(diào)返回一個(gè)新的 promise 對象

Promise.prototype.catch 方法: (onRejected) => {}
  1. onRejected 函數(shù): 失敗的回調(diào)函數(shù)(reason) => {} 践付。

說明: then()的語法糖, 相當(dāng)于: then(undefined, onRejected)

Promise.resolve 方法: (value) => {}
  1. value : 成功的數(shù)據(jù)或 promise 對象缺厉。

說明: 返回一個(gè)成功/失敗的 promise 對象永高。

 /**
   * Promise對象的resolve方法
   *  1. 接收一個(gè)參數(shù)返回一個(gè)成功和失敗的Promise對象
   *  2. 如果傳遞的參數(shù)為非Promise類型的對象,則返回的結(jié)果為成功的Promise對象
   *  3. 如果傳遞的參數(shù)是一個(gè)Promise對象提针,則參數(shù)的結(jié)果決定了 resolve的結(jié)果
   */
  let p1 = Promise.resolve('成功');
  console.log(p1);

  /**
   * 傳遞的參數(shù)是一個(gè)Promise對象 返回是成功
   * @type {Promise<any>}
   */
  let p2 = Promise.resolve(new Promise((resolve, reject) => {
    resolve('OK');
  }));
  console.log(p2);

  /**
   * 傳遞的參數(shù)是一個(gè)Promise對象 返回的是失敗
   * @type {Promise<any>}
   */
  let p3 = Promise.resolve(new Promise((resolve, reject) => {
    reject('Error');
  }));
  console.log(p3);

  p3.catch(reason => {
    console.log(reason); // Error 對處理失敗進(jìn)行處理
  });
Promise.reject 方法: (reason) => {}
  1. reason: 失敗的原因命爬。

說明: 返回一個(gè)失敗的 promise 對象。

/**
   * Promise中的reject方法
   *  1. reason:失敗的原因
   *  2. 如果傳遞的是一個(gè)非Promise對象辐脖,其結(jié)果是一個(gè)失敗的Promise
   *  3. 如果傳遞的是一個(gè)Promise,不管是返回成功的值還是失敗的值其處理結(jié)果都是失敗的Promise
   */
  let p1 = Promise.reject('失敗!');
  console.log(p1);

  let p2 = Promise.reject(new Promise((resolve, reject) => {
    resolve('成功!');
  }));
  console.log(p2);

  let p3 = Promise.reject(new Promise((resolve, reject) => {
    reject('Error');
  }));
  console.log(p3);
Promise.all 方法: (promises) => {}
  1. promises: 包含 n 個(gè) promise 的數(shù)組饲宛。

說明: 返回一個(gè)新的 promise, 只有所有的 promise 都成功才成功, 只要有一個(gè)失敗了就直接失敗。

  let p1 = new Promise((resolve, reject) => {
    resolve('OK!');
  });
  let p2 = Promise.resolve('Success!');
  let p3 = Promise.resolve('resolve!');
  let result = Promise.all([p1, p2, p3]); // 只有三個(gè)同時(shí)返回成功才會成功嗜价,否則將返回失敗
  console.log(result);
Promise.race 方法: (promises) => {}
  1. promises: 包含 n 個(gè) promise 的數(shù)組艇抠。

說明: 返回一個(gè)新的 promise, 第一個(gè)完成的 promise 的結(jié)果狀態(tài)就是最終的結(jié)果狀態(tài)幕庐。

/**
   * 返回一個(gè)新的Promise,第一個(gè)完成的Promise的結(jié)果狀態(tài)就是最終的狀態(tài)
   */
  let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('OK!');
    }, 1000);
  });
  let p2 = Promise.resolve('Success!');
  let p3 = Promise.resolve('resolve!');
  let result = Promise.race([p1, p2, p3]); // 只有三個(gè)同時(shí)返回成功才會成功家淤,否則將返回失敗
  console.log(result);
Promise 封裝讀取文件函數(shù)
/**
 * 封裝一個(gè)函數(shù) mineReadFile讀取文件內(nèi)容
 * 參數(shù): path文件路徑
 * 返回: Promise對象
 */
const fs = require('fs');

function mineReadFile(path) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, (err, data) => {
      if (err) reject(err);
      resolve(data);
    });
  });
}

mineReadFile('../resources/context.txt').then(value => {
    console.log(value.toString());
  },
  reason => {
    console.log(reason);
  });
const fs = require('fs');

/**
 * 1. 使用傳統(tǒng)的方式
 */
fs.readFile('../resources/context.txt', (err, data) => {
  if (err) throw new Error(err);
  console.log(data.toString());
});

// 2. 使用Promise的方式
let p = new Promise((resolve, reject) => {
  fs.readFile('../resources/context.txt', (err, data) => {
    if (err) reject(err);
    resolve(data.toString());
  });
});

p.then(value => {
    console.log('------------------------------------------------------------------------');
    console.log(value.toString());
  },
  reason => {
    console.log(reason);
  });
util.promisify簡化Promise
  1. util.promisifyNodeJSAPI 异剥。 參考網(wǎng)址
/**
 * 傳入一個(gè)遵循常見的錯(cuò)誤優(yōu)先的回調(diào)風(fēng)格的函數(shù)(即以(err,value)  => 回調(diào)作為最后一個(gè)參數(shù)),并返回一個(gè)Promise的版本
 */

// 引入util 模塊
const util = require('util'); // 引入util這個(gè)模塊

// 引入fs模塊
const fs = require('fs');

// 返回一個(gè)新函數(shù)
/**
 * util.promisify
 */
let mineReadFile = util.promisify(fs.readFile);

mineReadFile('../resources/context.txt').then(value => {
  console.log(value.toString());
});
1.3.2 promise 的幾個(gè)關(guān)鍵問題
如何改變 promise 的狀態(tài)?
  1. 開始Promise對象的初始化狀態(tài)是 pending絮重。

  2. resolve(value): 如果當(dāng)前是 pending就會變?yōu)?code>resolved届吁。

  3. reject(reason): 如果當(dāng)前是 pending 就會變?yōu)?rejected

  4. 拋出異常: 如果當(dāng)前是 pending 就會變?yōu)?rejected绿鸣。

 /**
   * 代碼中如何修改Promise對象的狀態(tài)
   */
  let p = new Promise((resolve,reject) => {
    // 1. 調(diào)用resolve函數(shù) 將對象的狀態(tài) 從 pending 修改為 fullfilled
    // resolve('OK');
    // 2. 調(diào)用reject函數(shù) pending => rejected
    // reject('Error');
    // 3. 拋出異常
    throw new Error('出問題了!');
  });
一個(gè) promise 指定多個(gè)成功/失敗回調(diào)函數(shù), 都會調(diào)用嗎?
  1. 當(dāng) promise 改變?yōu)閷?yīng)狀態(tài)時(shí)都會調(diào)用。
  /**
   * 為一個(gè)Promise對象指定多個(gè)回調(diào)暂氯,當(dāng)他們的狀態(tài)改變的時(shí)候都會調(diào)用潮模,如果一直是pending狀態(tài)的話就不會調(diào)用其回調(diào)
   */
  let p = new Promise((resolve, reject) => {
    resolve('OK');
  });

  p.then(value => {
    console.log(value);
  });

  p.then(value => {
    alert(value);
  });
改變 promise 狀態(tài)和指定回調(diào)函數(shù)誰先誰后?
  1. 都有可能, 正常情況下是先指定回調(diào)再改變狀態(tài), 但也可以先改狀態(tài)再指定回調(diào)。

  2. 如何先改狀態(tài)再指定回調(diào)?

在執(zhí)行器中直接調(diào)用 resolve()/reject()痴施。
延遲更長時(shí)間才調(diào)用 then() 擎厢。

  1. 什么時(shí)候才能得到數(shù)據(jù)?

如果先指定的回調(diào), 那當(dāng)狀態(tài)發(fā)生改變時(shí), 回調(diào)函數(shù)就會調(diào)用, 得到數(shù)據(jù)。
如果先改變的狀態(tài), 那當(dāng)指定回調(diào)時(shí), 回調(diào)函數(shù)就會調(diào)用, 得到數(shù)據(jù)辣吃。

promise.then()返回的新 promise 的結(jié)果狀態(tài)由什么決定?
  1. 簡單表達(dá): 由 then()指定的回調(diào)函數(shù)執(zhí)行的結(jié)果決定动遭。

  2. 詳細(xì)表達(dá):

如果拋出異常, 新 promise 變?yōu)?rejected, reason 為拋出的異常。
如果返回的是非 promise 的任意值, 新 promise 變?yōu)?resolved, value 為返回的值神得。
如果返回的是另一個(gè)新 promise, 此 promise 的結(jié)果就會成為新 promise 的結(jié)果厘惦。

promise 如何串連多個(gè)操作任務(wù)
  1. promise 的 then()返回一個(gè)新的 promise, 可以開成 then()的鏈?zhǔn)秸{(diào)用。

  2. 通過 then 的鏈?zhǔn)秸{(diào)用串連多個(gè)同步/異步任務(wù)哩簿。

promise 異常傳透?
  1. 當(dāng)使用 promise 的 then 鏈?zhǔn)秸{(diào)用時(shí), 可以在最后指定失敗的回調(diào)宵蕉。

  2. 前面任何操作出了異常, 都會傳到最后失敗的回調(diào)中處理。

中斷 promise 鏈?
  1. 當(dāng)使用 promise 的 then 鏈?zhǔn)秸{(diào)用時(shí), 在中間中斷, 不再調(diào)用后面的回調(diào)函數(shù)节榜。

  2. 辦法: 在回調(diào)函數(shù)中返回一個(gè) pendding 狀態(tài)的 promise 對象羡玛。


第 2 章 自定義(手寫) Promise

2.1 函數(shù)版本

/**
 * 執(zhí)行多個(gè)回調(diào)的實(shí)現(xiàn)
 * @param executor
 * @constructor
 */
function Promise(executor) {

  // 3. 添加屬性
  this.PromiseState = 'pending';
  this.PromiseResult = null;
  // 聲明一個(gè)屬性
  this.callBacks = [];

  // 預(yù)先保存this的值
  const _this = this;

  function resolve(data) {
    // 修改對象的狀態(tài) PromiseState 屬于實(shí)例對象的屬性
    // 設(shè)置結(jié)果值 PromiseResult 屬于實(shí)例對象的屬性
    // 對象狀態(tài)只允許修改一次
    if (_this.PromiseState !== 'pending') return;
    _this.PromiseState = 'fullfilled';
    _this.PromiseResult = data;
    // 在異步任務(wù)的時(shí)候,調(diào)用回調(diào)函數(shù)的時(shí)機(jī)
    // 循環(huán)遍歷執(zhí)行函數(shù)
    setTimeout(() => {
      _this.callBacks.forEach(item => {
        item.onResolved(data);
      });
    });
  }

  function reject(data) {
    // 對象狀態(tài)只允許修改一次
    if (_this.PromiseState !== 'pending') return;
    _this.PromiseState = 'rejected';
    _this.PromiseResult = data;
    // 在異步任務(wù)的時(shí)候宗苍,調(diào)用回調(diào)函數(shù)的時(shí)機(jī)
    /**
     * 循環(huán)遍歷執(zhí)行多個(gè)函數(shù)
     */
    setTimeout(() => {
      _this.callBacks.forEach(item => {
        item.onRejected(data);
      });
    });
  }

  // 執(zhí)行器函數(shù)式同步調(diào)用的
  // 調(diào)用執(zhí)行器函數(shù)
  try {
    executor(resolve, reject);
  } catch (e) {
    reject(e);
  }
}

// 1. 添加then方法
Promise.prototype.then = function (onResolved, onRejected) {
  // 保存this的值
  const _this = this;
  // 判斷回調(diào)函數(shù)的參數(shù) 是否為一個(gè)函數(shù)類型
  if (typeof onRejected !== 'function') {
    /**
     * 實(shí)現(xiàn)異常穿透的原理
     * @param reason
     */
    onRejected = reason => {
      throw reason;
    }
  }

  /**
   * Promise實(shí)現(xiàn)值傳遞
   */
  if (typeof onResolved !== 'function') {
    onResolved = value => value;
  }

  return new Promise((resolve, reject) => {

    function callBack(type) {
      /**
       * 當(dāng)出現(xiàn)異常的時(shí)候
       */
      try {
        // 獲取回調(diào)函數(shù)的執(zhí)行結(jié)果
        let result = type(_this.PromiseResult);
        if (result instanceof Promise) {
          // 如果是Promise類型的對象
          result.then(value => {
              resolve(value);
            },
            reason => {
              reject(reason);
            })
        } else {
          // 返回非Promise對象的時(shí)候狀態(tài)為成功
          resolve(result);
        }
      } catch (e) {
        reject(e);
      }
    }

    // 調(diào)用回調(diào)函數(shù) 成功失敗的狀態(tài)是 由PromiseState決定
    if (this.PromiseState === 'fullfilled') {
      setTimeout(() => {
        callBack(onResolved);
      });
    }

    if (this.PromiseState === 'rejected') {
      setTimeout(() => {
        callBack(onRejected);
      });
    }

    // 判斷 pending的狀態(tài)
    if (this.PromiseState === 'pending') {
      // 回調(diào)的執(zhí)行時(shí)機(jī)稼稿?
      // 保存回調(diào)函數(shù) 重要 重要 重要
      // 8. 實(shí)現(xiàn)執(zhí)行多個(gè)回調(diào)
      this.callBacks.push({
        onResolved: function () {
          callBack(onResolved);
        },
        onRejected: function () {
          callBack(onResolved);
        }
      });
    }
  });
};


// 12. 添加catch方法
Promise.prototype.catch = function (onRejected) {
  /**
   * 直接調(diào)用then方法
   */
  return this.then(undefined, onRejected);
};

// 13. 添加Promise.resolve方法
/**
 * Promise對象的 resolve 方法
 * @param value
 * @returns {Promise}
 */
Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    // 判斷value 的類型
    if (value instanceof Promise) {
      value.then(value => {
        resolve(value);
      }, reason => {
        reject(reason);
      });
    } else {
      resolve(value);
    }
  });
};

// 14. 添加 reject 方法
/**
 * 為Promise對象添加 reject 方法
 * @param reason
 * @returns {Promise}
 */
Promise.reject = function (reason) {
  return new Promise((resolve, reject) => {
    reject(reason);
  });
};

// 15. 添加all方法

/**
 * 為Promise對象添加 all 方法
 * @param promises
 * @returns {Promise}
 */
Promise.all = function (promises) {
  // 定義一個(gè)計(jì)數(shù)變量 計(jì)算當(dāng)前Promise成功的個(gè)數(shù)
  let count = 0;
  let arr = [];
  return new Promise((resolve, reject) => {
    // 遍歷 promises
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(value => {
          // 得知對象的狀態(tài)是成功
          // 狀態(tài)成功就會走到該函數(shù)中 進(jìn)行count++  操作
          count++;
          // 將當(dāng)前promise對象成功的結(jié)果存到一個(gè)數(shù)組中保持原有的順序
          arr[i] = value;
          // 判斷
          if (count === promises.length) {
            // 修改此時(shí)Promise的狀態(tài)
            resolve(arr);
          }
        },
        reason => {
          reject(reason);
        });
    }
  });
};


// 16 . race 方法 接受一個(gè)參數(shù) Promise 對象數(shù)組 其結(jié)果由數(shù)組中最先改變狀態(tài)的那個(gè)值決定

/**
 * Promise 對象的 race 方法
 * @param promises
 * @returns {Promise}
 */
Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(
        value => {
          resolve(value);
        },
        reason => {
          reject(reason);
        }
      );
    }
  });
};

// 17. 使用 setTimeOut 包裹代碼將其轉(zhuǎn)換為異步執(zhí)行的代碼

2.2 Class版本

class Promise {

  /**
   * 執(zhí)行多個(gè)回調(diào)的實(shí)現(xiàn)
   * @param executor
   * @constructor
   */
  constructor(executor) {
    // 3. 添加屬性
    this.PromiseState = 'pending';
    this.PromiseResult = null;
    // 聲明一個(gè)屬性
    this.callBacks = [];

    // 預(yù)先保存this的值
    const _this = this;

    function resolve(data) {
      // 修改對象的狀態(tài) PromiseState 屬于實(shí)例對象的屬性
      // 設(shè)置結(jié)果值 PromiseResult 屬于實(shí)例對象的屬性
      // 對象狀態(tài)只允許修改一次
      if (_this.PromiseState !== 'pending') return;
      _this.PromiseState = 'fullfilled';
      _this.PromiseResult = data;
      // 在異步任務(wù)的時(shí)候,調(diào)用回調(diào)函數(shù)的時(shí)機(jī)
      // 循環(huán)遍歷執(zhí)行函數(shù)
      setTimeout(() => {
        _this.callBacks.forEach(item => {
          item.onResolved(data);
        });
      });
    }

    function reject(data) {
      // 對象狀態(tài)只允許修改一次
      if (_this.PromiseState !== 'pending') return;
      _this.PromiseState = 'rejected';
      _this.PromiseResult = data;
      // 在異步任務(wù)的時(shí)候讳窟,調(diào)用回調(diào)函數(shù)的時(shí)機(jī)
      /**
       * 循環(huán)遍歷執(zhí)行多個(gè)函數(shù)
       */
      setTimeout(() => {
        _this.callBacks.forEach(item => {
          item.onRejected(data);
        });
      });
    }

    // 執(zhí)行器函數(shù)式同步調(diào)用的
    // 調(diào)用執(zhí)行器函數(shù)
    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  // then 方法
  then(onResolved, onRejected) {
    // 保存this的值
    const _this = this;
    // 判斷回調(diào)函數(shù)的參數(shù) 是否為一個(gè)函數(shù)類型
    if (typeof onRejected !== 'function') {
      /**
       * 實(shí)現(xiàn)異常穿透的原理
       * @param reason
       */
      onRejected = reason => {
        throw reason;
      }
    }

    /**
     * Promise實(shí)現(xiàn)值傳遞
     */
    if (typeof onResolved !== 'function') {
      onResolved = value => value;
    }

    return new Promise((resolve, reject) => {

      function callBack(type) {
        /**
         * 當(dāng)出現(xiàn)異常的時(shí)候
         */
        try {
          // 獲取回調(diào)函數(shù)的執(zhí)行結(jié)果
          let result = type(_this.PromiseResult);
          if (result instanceof Promise) {
            // 如果是Promise類型的對象
            result.then(value => {
                resolve(value);
              },
              reason => {
                reject(reason);
              })
          } else {
            // 返回非Promise對象的時(shí)候狀態(tài)為成功
            resolve(result);
          }
        } catch (e) {
          reject(e);
        }
      }

      // 調(diào)用回調(diào)函數(shù) 成功失敗的狀態(tài)是 由PromiseState決定
      if (this.PromiseState === 'fullfilled') {
        setTimeout(() => {
          callBack(onResolved);
        });
      }

      if (this.PromiseState === 'rejected') {
        setTimeout(() => {
          callBack(onRejected);
        });
      }

      // 判斷 pending的狀態(tài)
      if (this.PromiseState === 'pending') {
        // 回調(diào)的執(zhí)行時(shí)機(jī)让歼?
        // 保存回調(diào)函數(shù) 重要 重要 重要
        // 8. 實(shí)現(xiàn)執(zhí)行多個(gè)回調(diào)
        this.callBacks.push({
          onResolved: function () {
            callBack(onResolved);
          },
          onRejected: function () {
            callBack(onResolved);
          }
        });
      }
    });
  }

  // catch 方法
  catch(onRejected) {
    /**
     * 直接調(diào)用then方法
     */
    return this.then(undefined, onRejected);
  }


  /**
   * Promise對象的 resolve 方法
   * @param value
   * @returns {Promise}
   */
  static resolve(value) {
    return new Promise((resolve, reject) => {
      // 判斷value 的類型
      if (value instanceof Promise) {
        value.then(value => {
          resolve(value);
        }, reason => {
          reject(reason);
        });
      } else {
        resolve(value);
      }
    });
  };


  /**
   * 為Promise對象添加 reject 方法
   * @param reason
   * @returns {Promise}
   */
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  };


  /**
   * 為Promise對象添加 all 方法
   * @param promises
   * @returns {Promise}
   */
  static all(promises) {
    // 定義一個(gè)計(jì)數(shù)變量 計(jì)算當(dāng)前Promise成功的個(gè)數(shù)
    let count = 0;
    let arr = [];
    return new Promise((resolve, reject) => {
      // 遍歷 promises
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(value => {
            // 得知對象的狀態(tài)是成功
            // 狀態(tài)成功就會走到該函數(shù)中 進(jìn)行count++  操作
            count++;
            // 將當(dāng)前promise對象成功的結(jié)果存到一個(gè)數(shù)組中保持原有的順序
            arr[i] = value;
            // 判斷
            if (count === promises.length) {
              // 修改此時(shí)Promise的狀態(tài)
              resolve(arr);
            }
          },
          reason => {
            reject(reason);
          });
      }
    });
  };


  /**
   * Promise 對象的 race 方法
   * @param promises
   * @returns {Promise}
   */
  static race(promises) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          value => {
            resolve(value);
          },
          reason => {
            reject(reason);
          }
        );
      }
    });
  };
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市挪钓,隨后出現(xiàn)的幾起案子是越,更是在濱河造成了極大的恐慌,老刑警劉巖碌上,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倚评,死亡現(xiàn)場離奇詭異浦徊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)天梧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進(jìn)店門盔性,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呢岗,你說我怎么就攤上這事冕香。” “怎么了后豫?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵悉尾,是天一觀的道長。 經(jīng)常有香客問我挫酿,道長构眯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任早龟,我火速辦了婚禮惫霸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘葱弟。我一直安慰自己壹店,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布芝加。 她就那樣靜靜地躺著硅卢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪藏杖。 梳的紋絲不亂的頭發(fā)上老赤,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機(jī)與錄音制市,去河邊找鬼抬旺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛祥楣,可吹牛的內(nèi)容都是我干的开财。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼误褪,長吁一口氣:“原來是場噩夢啊……” “哼责鳍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起兽间,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤历葛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體恤溶,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乓诽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了咒程。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸠天。...
    茶點(diǎn)故事閱讀 39,977評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖帐姻,靈堂內(nèi)的尸體忽然破棺而出稠集,到底是詐尸還是另有隱情,我是刑警寧澤饥瓷,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布剥纷,位于F島的核電站,受9級特大地震影響呢铆,放射性物質(zhì)發(fā)生泄漏筷畦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一刺洒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吼砂,春花似錦逆航、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至周偎,卻和暖如春抹剩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蓉坎。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工澳眷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛉艾。 一個(gè)月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓钳踊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親勿侯。 傳聞我的和親對象是個(gè)殘疾皇子拓瞪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1、準(zhǔn)備知識 1助琐、回調(diào)函數(shù): 理解:一個(gè)函數(shù)祭埂,自己定義的,自己沒有調(diào)用兵钮,但是函數(shù)執(zhí)行了蛆橡。 同步回調(diào):立即執(zhí)行舌界,完全...
    Shy啊閱讀 1,127評論 0 1
  • Promise是什么 Promise是異步編程的一種解決方案禀横。具體表達(dá):語法上來說,Promise是一個(gè)構(gòu)造函數(shù)功...
    麥西的西閱讀 569評論 0 1
  • 首先我們得先理解Promise工作的機(jī)制粥血,其實(shí)Promise就是一個(gè)構(gòu)造函數(shù) 它的內(nèi)部有三個(gè)狀態(tài): pending...
    Poppy11閱讀 11,020評論 0 6
  • Promise 含義 Promise 是異步編程的一種解決方案柏锄,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)...
    Upcccz閱讀 221評論 0 0
  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,170評論 0 16