天天看點

使用ES6中Class實作手寫PromiseA+,完美通過官方872條用例

使用ES6中Class實作手寫PromiseA+,完美通過官方872條用例

今天這篇文章主要帶領你手寫一個Promise源碼,學完你就會發現:Promise沒有你想象中的那麼難,如果對Promise源碼不是很清楚,還是推薦從頭看,相信你認真從頭看到尾,并且去實際操作了,肯定會有收獲的。

主要是代碼部分有點多,不過好多都是重複的,不必擔心。

Promise出現的原因

promise出現的原因,說白了也就是promise解決了什麼問題,這裡我就簡單說點吧:

Promise 是異步程式設計的一種解決方案,比傳統的解決方案回調函數和事件更合理和更強大,Promise的出現主要解決以下兩個問題:

  1. 回調地獄: 某個異步操作需要等待之前的操作完成在繼續執行, 當這樣的需求多了以後, 使的代碼進入無盡的嵌套,可讀性降低不好維護。
  2. 異步和同步之間的聯系問題:當一個同步操作需要等待多個異步操作的結果, 這樣會使得代碼的邏輯變得相對來說比較複雜。

myPromise的實作要點

  1. Promise 就是一個類,實際上就是ES6提供的一個新的構造函數,
  2. 通過new建立執行個體,接收一個函數作為參數,并且該函數中的代碼預設是同步代碼,會立即執行
  3. Promise 有三種狀态,成功resolved,失敗rejected,等待pedding
  4. Promise 狀态一旦發生改變,就不能在改變,狀态不可逆
  5. 使用者可自定義成功的資料及失敗的原因
  6. Promise 執行個體擁有一個then方法。接受兩個參數,一個成功回調,一個失敗回調
  7. executor執行器在執行過程中,可能會抛出異常 throw new Error(),需要 try catch 捕獲
  8. 當執行器中的是異步代碼時(如定時器執行resolve),不是立即執行 狀态不會變更,此時then方法中狀态仍然是pending
  9. 一個 promise 執行個體可以被 then 多次,分别建立一個隊列用來存放成功和失敗回調事件
  10. 定時器執行時,判斷狀态為 pending,非立即執行,而是先存放 成功/失敗 回調事件
  11. 等待狀态變為成功或失敗時周遊隊列依次執行對應的onfulfilled和onrejected,并且有執行順序
  12. Promise 實作鍊式調用,傳回的并不是this,而是一個新的promise執行個體, 因為原有的promise狀态一旦發生改變,就不能再改變,否則不符合規範
  13. Promise 成功和失敗的回調的傳回值,可以傳遞給下一次的的then
  • 傳回的是普通值:不論then是成功還是失敗。隻要是普通值就傳遞到下一次then的成功中。(普通值包括:非錯誤非promise,包括對象)
  • 報錯或異常:一定會走到下一次then的失敗中。如果離自己最近的then沒有錯誤處理,會向下找
  • 特别注意: return new Error()傳回的是錯誤對象,屬于普通值走下一次then的成功,而throw new Error() 是抛出異常,需要try catch 會走下一次的失敗
  • 傳回的是promise:會采用promise的狀态,決定下一次then是成功還是失敗,此處會有遞歸判斷
  • 總結: 如果傳回一個普通值,除了promise ,就傳遞給下一個then的成功,如果傳回一個失敗的promise或者抛出異常,會走下一個then的失敗。
  1. Promise 值的穿透,當一個promise連續then 多次,并且then中沒有傳回任何值時,此時data會穿透至最後一個then中
  • 14.1 執行onfulfilled時,先判斷是不是一個function,如果是就直接執行,反之就包一層函數
  • 14.2 執行onrejected時,先判斷是不是一個function,如果是就直接執行,反之就抛出失敗異常
  • 既然promise是為了解決異步回調函數嵌套問題,那麼可以通過promise的延遲對象來減少一層嵌套關系
  • promiseA+ 規範測試
  • 聊點規範以外的東東吧~~catch, finally,resolve,reject,all,race,就這麼多

myPromise的實作

下面就來實作以上要點

myPromise — 實作簡單的同步

myPromise.js

// 要點 3: 有三種狀态, 相當于是常量,可以放在外面
const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
// 要點 1: Promise 就是一個類
class Promise {
  // 要點 2: 接收一個函數executor作為參數,立即執行
  constructor(executor) {
    this.status = PENDING
    this.value = undefined  // 要點 5: 使用者可自定義 成功的資料
    this.reason = undefined // 要點 5: 使用者可自定義 失敗的原因
    let resolve = (value) => {
      // 要點 4: 狀态不可逆, 隻有PENDING時,才可以改變狀态
      if (this.status === PENDING) {
        this.value = value       // 成功的資料
        this.status = RESOLVED   // 狀态置為 RESOLVED
      }
    }
    let reject = (reason) => {
      // 要點 4: 狀态不可逆, 隻有PENDING時,才可以改變狀态
      if (this.status === PENDING) {
        this.reason = reason     // 失敗的原因
        this.status = REJECTED   // 狀态置為 REJECTED
      }
    }


    // 要點 7: 錯誤處理,抛出異常 throw new Error()
    try {  
      executor(resolve, reject) // 立即執行
    } catch(e) {   // 抛出異常 直接失敗
      reject(e)
    }
  }
  // 要點 6: 擁有一個then方法(執行個體上的方法)。接受兩個參數回調函數
  then(onfulfilled, onrejected) {
    if(this.status === RESOLVED){
      onfulfilled(this.value)
    }
    if(this.status === REJECTED){
      onrejected(this.reason)
    }
  }
}
// Commonjs規範導出子產品
module.exports = Promise      

test.js 簡單的同步操作驗證

// 使用自己的promise,注釋該行代碼,就是原生promise
let Promise = require('./1.myPromise')


let p = new Promise((resolve, reject) => {
  resolve('成功')
  // throw new Error('失敗了')
  // reject('失敗')
})


p.then((data) => {
  console.log(data)
}, (err) => {
  console.log(err)
})      

myPromise — 增加異步功能

在實作了簡單的同步操作後,再來看看異步代碼的執行,還是使用上面 test 中的案例,簡單的改造一下,寫一個定時器模拟異步環境,test.js:增加異步功能。

let Promise = require('./1.myPromise')


let p = new Promise((resolve, reject) => {
  setTimeout(()=>{
      resolve('成功')
  },1000)
})


p.then((data) => {
   console.log(data,1)
}, (err) => {
   console.log(err)
})
// 一個執行個體可以then多次,執行結果會是一樣的,因為狀态已經固定
p.then((data) => {
   console.log(data,2)
}, (err) => {
   console.log(err)
})      

myPromise.js

const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
class Promise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined   
    this.reason = undefined 
    // 要點 9: 分别建立一個`隊列用來存放成功和失敗回調事件
    this.onfulfilledCallbacks = []
    this.onrejectedCallbacks = []
    let resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value
        this.status = RESOLVED
        this.onfulfilledCallbacks.forEach(fn => fn())  // 要點 11: 狀态變為成功時周遊隊列依次執行
      }
    }
    let reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
        this.onrejectedCallbacks.forEach(fn => fn())   // 要點 11: 狀态變為失敗時周遊隊列依次執行
      }
    }


    try {  
      executor(resolve, reject) 
    } catch(e) { 
      reject(e)
    }
  }
  then(onfulfilled, onrejected) {
    if(this.status === RESOLVED){
      onfulfilled(this.value)
    }
    if(this.status === REJECTED){
      onrejected(this.reason)
    }
    // console.log(this.status)   // PENDING
    // 要點 8: 定時器執行resolve時,狀态仍然是pending
    if(this.status === PENDING){
      // 要點 10: 一個 promise 執行個體可以被 then 多次,存放 成功回調事件
      this.onfulfilledCallbacks.push(() => {
        onfulfilled(this.value)
      })
      // 要點 10: 一個 promise 執行個體可以被 then 多次,存放 失敗回調事件
      this.onrejectedCallbacks.push(() => {
        onrejected(this.reason)
      })
    }
  }
}
// Commonjs規範導出子產品
module.exports = Promise      

myPromise — 鍊式調用(重難點,不好了解)

鍊式調用這一部分,比較繞,不太好了解,我的老師鼓勵我,說:“書讀百遍,其義自現”,哈哈哈,現在用來鼓勵大家吧!!!test.js。

let Promise = require('./1.myPromise')


let p = new Promise((resolve, reject) => {
  resolve('成功')
})


// 可以分别注釋用例代碼,進行效果示範
p.then((data) => {
  return data              // 用例1. 傳回的是普通值: ‘成功’
  // throw new Error()     // 用例2. 報錯或異常: 抛出異常,會直接走下一次then的失敗
  // return new Error()    // 用例3. 傳回的是普通值: error對象,需要特别注意
  // return new Promise((s,j)=>s(1))    // 用例4. 傳回的是promise: 會傳遞目前promise的已成功結果
  // return new Promise((s,j)=>j(1))    // 用例5. 傳回的是promise: 會傳遞目前promise的已失敗結果
}).then((data) => {
  console.log(data,2)      // 執行結果:用例1, 用例3, 用例4 
}, (err) => {
  console.log(err,3)       // 執行結果:用例2, 用例5
})


// 1. 傳回的是普通值:不論then是成功還是失敗。隻要是普通值就傳遞到下一次then的成功中。(普通值包括:非錯誤非promise,包括對象)
// 2. 報錯或異常:一定會走到下一次then的失敗中。如果離自己最近的then沒有錯誤處理,會向下找
// 3. 特别注意: return new Error()傳回的是錯誤對象,屬于普通值走下一次then的成功,而throw new Error() 是抛出異常,會走下一次的失敗
// 4. 傳回的是promise:會采用promise的狀态,決定下一次then是成功還是失敗,此處會有遞歸判斷
// 5. 總結: 如果傳回一個普通值,除了promise ,就傳遞給下一個then的成功,如果傳回一個失敗的promise或者抛出異常(try catch),會走下一個then的失敗      

myPromise.js

const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
const resolvePromise = (promise2, x, resolve, reject) => {
  if (promise2 === x) { // 防止自己等待自己 一直循環等待 
    // 原生傳回:return reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
    return reject(new TypeError('我們自己報的錯:循環引用報錯'))
  }
  let called; // 為了相容其他promise 庫符合a+規範,防止狀态改變後再次被調用
  // 考慮其他promise庫相容問題:判斷x是不是promise:不是null的對象  || 函數  
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try { // 為防止then時 出現異常,Object.defineProperty
      let then = x.then // 取x 的then 方法 
      /**
       * 可能對象是{then:{}},還需要判斷then是否是函數,
       * 是函數就認為是promise,并調用then方法
       */
      if (typeof then === 'function') {
        // call第一個參數是this,後面的是成功回調 和 失敗回調
        // 如果成功回調傳回值 y 仍然是promise,就繼續遞歸解析promise,解析到傳回值不是一個Promise類型為止
        // promise2能否成功,是根據x的值來定的,x是promise,那麼就要等到x完成,x完成後,如果傳回y又是promise,那promise2又要等到y完成
        then.call(x, y =>{
          if (called) return
          called = true
          resolvePromise(promise2, y, resolve, reject)
        }, e => { 
          if (called) return
          called = true
          reject(e) 
        })
      } else {    
        // 否則就認為 then 是一個普通對象,成功即可
        resolve(x)
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)   // 出現異常,直接走失敗邏輯
    }
  } else { // x 為普通值
    resolve(x)
  }
}
class Promise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    this.onfulfilledCallbacks = []
    this.onrejectedCallbacks = []
    let resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value
        this.status = RESOLVED
        this.onfulfilledCallbacks.forEach(fn => fn())
      }
    }
    let reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
        this.onrejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }


  then(onfulfilled, onrejected) {
    // 要點 12: then鍊式調用,傳回的并不是this,而是一個`新的promise執行個體`
    let promise2 = new Promise((resolve, reject) => { // new Promise執行器中的代碼是立即執行,是以沒有影響
      if (this.status === RESOLVED) {
        // 加定時器是因為在目前執行上下文中,不能擷取promise2的值
        setTimeout(() => { 
          // 要點13.2. 用于報錯或抛出異常處理,以下同理
          try { 
            // 要點13:存儲第一次then的回調結果傳回值,用于下次then的參數傳遞,以下let x 同理
            let x = onfulfilled(this.value)
            // 要點13.3: x 可能會是一個promise, 需要單獨處理,
            // 并将promise2、新的傳回值:x 、成功回調resolve  失敗回調reject 作為參數傳遞
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            // 要點13.2. 報錯或抛出異常處理,直接走promise2的reject,以下同理
            reject(e) 
          }
        })
      }
      // 以下邏輯同理
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onrejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === PENDING) {
        // 暫存成功回調隊列
        this.onfulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onfulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
        // 暫存失敗回調隊列
        this.onrejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onrejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return promise2 // 要點 12: 傳回一個新的promise執行個體
  }
}


// Commonjs規範導出子產品
module.exports = Promise      

myPromise — 值的透傳

Promise 值的穿透,當一個promise連續then多次 ,并且then中沒有傳回任何值時,此時data會穿透值最後一個then中test.js。

//原生方法:值的穿透 示範
let p = new Promise((resolve,reject)=>{
  resolve(1)
  // reject(2)   // 同理
})
p.then().then().then(data=>{
  console.log(data);   // 1
})
p.then().then().then(null,err=>{
  console.log(err);    // 2
})


// ----------------------------------------------------------


//myPromise:基于以上幾部分的實作,我們自己的promise是不支援穿透的,那麼看一下演變過程,就很容易寫出符合規範的透傳
const Promise = require('./1.myPromise')
let p1 = new Promise((resolve,reject)=>{
  resolve(1)
})
let p2 = new Promise((resolve,reject)=>{
  reject(2)   // 同理,
})


// 相當于, 啥都不寫時,感覺像是預設定義了一個函數,不停向下傳遞,去實作一下吧~
p1.then(data => data)
  .then(data => data)
  .then(data =>{
    console.log(data);
  })


// 相當于, throw err,不停向下傳遞
p2.then(null,err=>{
  throw err
}).then(null,err=>{
  console.log(err);
  throw err 
}).then(null,err=>{
  console.log(err);
})      

myPromise.js

上面重複的代碼是在是太多了,就不在贅述了,怕看着更暈了,來, 找到類中then方法定義的位置,加上下面兩行代碼,就可以完美實作成功及失敗的資料透傳。

...
  ...
  ...
  // 在這裡這裡...其他都不變
  then(onfulfilled, onrejected) {
    // onfulfilled 是函數就執行,不是函數包一層函數并直接傳回資料
    onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : v => v;
    // onrejected 是函數就執行,不是函數包一層函數并直接抛出錯誤
    onrejected = typeof onrejected === 'function' ? onrejected : err => { throw err };
  ...
  ...
  ...      

完美通過官方872條用例

為了測試myPromise庫,必須公開一個非常小的擴充卡接口。可以說是promise的延遲對象defer,下文會詳細介紹,各位稍安勿躁。

  1. 第一步:找到檔案最下面。加上下面幾行代碼,
/**
   *   ...
   * 其餘不變
   *   ...
   */
// 測試自己的寫的promise 是否符合a+規範
// promise的延遲對象
Promise.defer = Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject
  })
  return dfd;
}


module.exports = Promise      

第二步:安裝測試包 npm install promises-aplus-tests -g

第三步:進入對應目錄執行 promises-aplus-tests ./1.myPromise.js

使用ES6中Class實作手寫PromiseA+,完美通過官方872條用例

如果你的測試結果和我圖中标注一樣,那麼恭喜你,如果驗證不通過,肯定是有一些不一樣,注意看報錯資訊,修正一下就好了。

myPromise的延遲對象defer用法

promise是為了解決異步回調函數嵌套問題,那麼可以通過promise的延遲對象來減少一層嵌套關系

模拟資料準備,準備以下兩個檔案,

  • name.txt:檔案内容是:age.txt
  • age.txt:檔案内容18

需求:首先通過fs子產品讀取到name中的内容(讀到新的filepath),再去讀取age.txt中的内容并輸出。

沒有延遲對象的寫法:

const fs = require('fs')
const Promise = require('./1.myPromise')


function read(filepath){
  // 想要read方法可以使用then方法,需要包一層new Promise
  return new Promise((resolve,reject)=>{   
    fs.readFile(filepath,'utf8',function(err,data){
      if(err) return reject(err)
      resolve(data)
    })
  })
}


read('./name.txt').then(data=>{
  return read(data)    // data = age.txt
}).then(data=>{
  console.log(data);   // data = age.txt
})      

使用延遲對象的寫法:

const fs = require('fs')
const Promise = require('./1.myPromise')


function readDefer(filepath){
  let dfd = Promise.defer()   // 減少一層嵌套
  fs.readFile(filepath,'utf8',function(err,data){
    if(err) return dfd.reject(err)
    dfd.resolve(data)
  })
  return dfd.promise
}


readDefer('./name.txt').then(data=>{
  return readDefer(data) // data = age.txt
}).then(data=>{
  console.log(data);    // data = age.txt
})      

myPromise.catch

完成以上内容,就已經實作了Promise A+ 規範的全部内容,而我們常用的catch及finally并不屬于 A+規範,同樣可以實作一下,catch是屬于執行個體上的方法,用于出錯處理及捕獲異常

演變過程:test.js

const Promise = require('./1.myPromise')


new Promise((resolve, reject) => {
  reject('失敗') 
}).then(data => {         // 第一個then沒有錯誤處理方法,直接會傳遞到第二個then中
  console.log(data);
}).then(null, err => {
  console.log(err, 'errrrrr');     // 失敗 errrrrr
})


// 将上述代碼中null優化一下,于是就有了catch方法
new Promise((resolve, reject) => {
  reject('失敗') 
}).then(data => {         
  console.log(data);
}).catch(err => {
  console.log(err, 'errrrrr');     // 失敗 errrrrr
})      

實作:myPromise.js

class Promise{
  // 重複代碼省略
  // constructor(){...}
  // then(){...}


  catch(onrejected){    // 執行個體的catch
    return this.then(null,onrejected)
  }
}
/**
 * 其餘重複代碼省略
 */      

myPromise.finally

finally方法是屬于執行個體上的方法.表示無論成功還是失敗,都會執行,并且可以向下執行。

了解起來有點點繞哈,建議多看幾遍吧,test.js

const Promise = require('./1.myPromise')


let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    // resolve('成功')  // 1
    reject('失敗')      // 2
  }, 1000)
}).finally(() => { 
  return new Promise((resolve, reject) => { 
    setTimeout(() => {
      // resolve(1000)  // 3 不會采用promise成功的結果,但是會等待執行
      reject(9000)      // 4 但是傳回一個失敗的promise,會走catch,相當于throw new Error
    }, 1000)
  })
}).then(data => {
  console.log(data);      // 對應上述不同序号組合,輸出結果有所不同,可自行組合實驗,下方也會給大家作出總結
}).catch(e => {
  console.log(e, 'catch');
})


/**
 * 整理流程:
 * 組合:
 * 1+3:成功 + 成功 ,走then=>data 傳回 1 傳回的資料('成功')
 * 1+4:成功 + 失敗 ,走then=>err 抛出 4 傳回的資料(9000)
 * 2+3:失敗 + 成功 ,走then=>err 抛出 2 傳回的資料('失敗')
 * 2+4:失敗 + 失敗 ,走then=>err 抛出 4 傳回的資料(9000)
 */      

實作:myPromise.js

/**
 * 1. finally 傳遞了一個方法 callback
 * 2. finally 方法相當于一個then,特點就是在函數中傳回一個promise,并會等待這個promise的完成
 * 3. Promise.resolve 又會等待 callback執行完成
 */


class Promise{
  // 重複代碼省略
  // constructor(){...}
  // then(){...}
  // catch(){...}
  finally(cb){     
    return this.then((data)=>{   // 成功 || 失敗 都會執行
      // Promise.resolve 目的是等待cb() 後的promise完成
      // 成功時調用finally,直接傳遞this執行個體成功的資料data,finally方法本身是沒有參數的
      return Promise.resolve(cb()).then(()=>data)  
    },(err)=>{
      // 失敗時調用finally,等待Promise.resolve(cb())的執行的結果,當cb結果為失敗時,直接以失敗的結果直接走catch
      // 而隻有在Promise.resolve成功時候,才會去執行.then(()=>{throw err}),抛出this執行個體reject的err資訊
      return Promise.resolve(cb()).then(()=>{throw err})
    })
  }
}
/**
 * 其餘重複代碼省略
 */      

myPromise.resolve

Promise.resolve() 方法會創造一個成功的promsie,屬于類上的方法,通過className調用

演變過程:test.js

const Promise = require('./1.myPromise')
Promise.resolve(200).then(data => {
  console.log(data);     // 200
})
// 等價于
new Promise((resolve, reject) => {
  resolve(200)
}).then(data => {
  console.log(data);     // 200
})      

實作:myPromise.js

constructor(executor) {
  /**
   * 其餘重複無改動
   */
  let resolve = (value) => {
    // 一個promise直接resolve 另一個promise時,會等待裡面的promise執行完,傳回成功的執行結果
    if (value instanceof Promise) {
      return value.then(resolve, reject)
    }
    if (this.status === PENDING) {
      this.value = value
      this.status = RESOLVED
      this.onfulfilledCallbacks.forEach(fn => fn())
    }
  }
  let reject = (reason) => {
    // reject 一個promise時,不需要單獨處理,失敗就直接走失敗的邏輯,不用處理其傳回值
    if (this.status === PENDING) {
      this.reason = reason
      this.status = REJECTED
      this.onrejectedCallbacks.forEach(fn => fn())
    }
  }
  /**
   * 其餘重複無改動
   */
}
static resolve(value) {
  return new Promise((resolve, reject) => {
    resolve(value)
  })
}      

myPromise.reject

Promise.reject() 方法會創造一個失敗的promsie,屬于類上的方法,通過className調用。

演變過程:test.js

const Promise = require('./1.myPromise')


Promise.reject(100).then(null,err => {
  console.log(err, 'errrrrr');   // 100 errrrrr
})


// 等價于
new Promise((resolve, reject) => {
  reject(100)
}).then(data=>{
  console.log(data);     
},err => {
  console.log(err, 'errrrrr');    // 100 errrrrr
})      

實作:myPromise.js

class Promise{
  /**
   * 其餘重複無改動
   */
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }
}
/**
 * 其餘重複無改動
 */      

myPromise.all

Promise.all()方法用于将多個 Promise 執行個體,包裝并傳回一個新的 Promise 執行個體p

p的狀态由傳遞的多個 Promise 決定,分成兩種情況。(官網是這樣說的)

(1)隻有所有promise執行個體的狀态都變成fulfilled,p的狀态才會變成fulfilled,此時數組中promise的傳回值組成一個數組,傳遞給p的回調函數。

(2)隻要數組的中promise有一個被rejected,p的狀态就變成rejected,此時第一個被reject的執行個體的傳回值,會傳遞給p的回調函數。

test.js

const Promise = require('./1.myPromise')
Promise.all([
  1, 2, 3, 
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功')
    }, 1000)
  }), 
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功')    // 情況1
      // reject('失敗')  // 情況2
    }, 1000)})
  ]).then(data => {
    console.log(data);   // 情況1. [ 1, 2, 3, '成功', '成功' ]
  }).catch(err => {
    console.log(err);    // 情況2. 失敗
  })      

myPromise.js

class Promise{
  // 在原有代碼基礎上,添加一個靜态方法
  static all(promises){
    return new Promise((resolve,reject)=>{
      let result = []
      let times = 0
      const processSuccess = (index,val)=>{
        result[index] = val
        if(++times === promises.length){
          resolve(result)
        }
      }
      for (let i = 0; i < promises.length; i++) { // 并發。多個請求一起執行
        let p = promises[i];
        if(p && typeof p.then === 'function'){   // 判斷p是不是promise執行個體,是則繼續等待p的執行結果,處理p對應的成功和失敗
          p.then(data=>{
            processSuccess(i,data)
          },reject) // 如果其中一個promise失敗了,直接執行失敗回調,由于reject本身就是函數,故可簡寫
        }else{   // 如果不是promise執行個體就直接調用成功,并存儲該值
          processSuccess(i,p)
        }
      }
    })
  }
}      

myPromise.race

Promise.race()方法同樣是将多個 Promise 執行個體,包裝并傳回一個新的 Promise 執行個體p。

隻要有一個執行個體率先改變狀态,p的狀态就跟着改變。那個率先改變的 Promise 執行個體的傳回值,就傳遞給p的回調函數。

(簡單了解:race 賽跑,采用最快的那一個,race方法如果其中一個完成了,其他方法還是會執行完,隻是并沒有采用他的結果)

test.js

const Promise = require('./1.myPromise')
Promise.race([ 
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('成功')
    }, 2000)
  }), 
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('失敗')
    }, 1000)
  })
]).then(data => {
  console.log(data);
}).catch(err => {
  console.log(err);   // 輸出:失敗,resolve 2s執行,reject 1s執行,reject更快
})      

myPromise.js

class Promise{
  static race(promises){
    return new Promise((resolve,reject)=>{
      // for循環執行,隻要有一個狀态發生改變,就直接将該執行個體的傳回值 傳回
      for (let i = 0; i < promises.length; i++) {
        let p = promises[i];
        if(p && typeof p.then === 'function'){
          p.then(data=>{
            resolve(data)
          },reject)
        }else{
          resolve(p)
        }
      }
    })
  }
}      

myPromise終極版本

const RESOLVED = 'RESOLVED'
const REJECTED = 'REJECTED'
const PENDING = 'PENDING'
const resolvePromise = (promise2, x, resolve, reject) => {
  if (promise2 === x) {
    return reject(new TypeError('循環引用報錯'))
  }
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {


    let called;
    try {
      let then = x.then
      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) return
          called = true
          resolvePromise(promise2, y, resolve, reject)
        }, e => {
          if (called) return
          called = true
          reject(e)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)
    }
  } else {
    resolve(x)
  }
}
class Promise {
  constructor(executor) {
    this.status = PENDING
    this.value = undefined
    this.reason = undefined
    this.onfulfilledCallbacks = []
    this.onrejectedCallbacks = []
    let resolve = (value) => {
      if (value instanceof Promise) {
        return value.then(resolve, reject)
      }
      if (this.status === PENDING) {
        this.value = value
        this.status = RESOLVED
        this.onfulfilledCallbacks.forEach(fn => fn())
      }
    }
    let reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
        this.onrejectedCallbacks.forEach(fn => fn())
      }
    }


    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  then(onfulfilled, onrejected) {
    onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : v => v;
    onrejected = typeof onrejected === 'function' ? onrejected : err => {
      throw err
    };
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === RESOLVED) {
        setTimeout(() => {
          try {
            let x = onfulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onrejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      if (this.status === PENDING) {
        this.onfulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onfulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
        this.onrejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onrejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      }
    })
    return promise2
  }
  catch (onrejected) {
    return this.then(null, onrejected)
  } 
  finally(cb) {
    return this.then(
      (data) => {
        return Promise.resolve(cb()).then(() => data)
      }, (err) => {
        return Promise.resolve(cb()).then(() => { throw err })
      })
  }
  static resolve(value) {
    return new Promise((resolve, reject) => {
      resolve(value)
    })
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }
  static all(promises) {
    return new Promise((resolve, reject) => {
      let result = []
      let times = 0
      const processSuccess = (index, val) => {
        result[index] = val
        if (++times === promises.length) {
          resolve(result)
        }
      }
      for (let i = 0; i < promises.length; i++) {
        let p = promises[i];
        if (p && typeof p.then === 'function') {
          p.then(data => {
            processSuccess(i, data)
          }, reject)
        } else {
          processSuccess(i, p)
        }
      }
    })
  }
  static race(promises) {
    return new Promise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        let p = promises[i];
        if (p && typeof p.then === 'function') {
          p.then(data => {
            resolve(data)
          }, reject)
        } else {
          resolve(p)
        }
      }
    })
  }
}


Promise.defer = Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve;
    dfd.reject = reject
  })
  return dfd;
}


module.exports = Promise      

Click Me