如何手動實現一個Promise.all

我相信有些同窗在面試的過程當中,面試官會問你可否簡單描述一下Promise.all的原理,小編我也是被問到,因而也在網上找了幾遍文章和例子,看完以後也是死恍然大悟,但願能幫到小夥伴們git

Promise.all原理

Promise.all 接收一個 promise 對象的數組做爲參數,當這個數組裏的全部 promise 對象所有變爲resolve或 有 reject 狀態出現的時候,它纔會去調用 .then 方法,它們是併發執行的github

在實現以前先解讀promise.all 的特色

一、Promise.all()方法將多個Promise實例包裝成一個Promise對象(p),接受一個數組(p1,p2,p3)做爲參數,數組中不必定須要都是Promise對象,可是必定具備Iterator接口,若是不是的話,就會調用Promise.resolve將其轉化爲Promise對象以後再進行處理。面試

二、使用Promise.all()生成的Promise對象(p)的狀態是由數組中的Promise對象(p1,p2,p3)決定的;數組

  • 一、若是全部的Promise對象(p1,p2,p3)都變成fullfilled狀態的話,生成的Promise對象(p)也會變成fullfilled狀態,p1,p2,p3三個Promise對象產生的結果會組成一個數組返回給傳遞給p的回調函數;
  • 二、若是p1,p2,p3中有一個Promise對象變爲rejected狀態的話,p也會變成rejected狀態,第一個被rejected的對象的返回值會傳遞給p的回調函數。

Promise.all()方法生成的Promise對象也會有一個catch方法來捕獲錯誤處理,可是若是數組中的Promise對象變成rejected狀態時,而且這個對象還定義了catch的方法,那麼rejected的對象會執行本身的catch方法,而且返回一個狀態爲fullfilled的Promise對象,Promise.all()生成的對象會接受這個Promise對象,不會返回rejected狀態promise

版本一

function PromiseAll(arr) {
    //PromiseAll的返回值爲一個promise對象
    return new Promise((resolve, reject) => {
        //PromiseAll的入參必須是數組
        if (!Array.isArray(arr)) {
            return reject(new TypeError('arr must be an array.'));
        }
        let resArr = [];
        for (let i in arr) {
            (function(i) {
                Promise.resolve(arr[i]).then(res => {
                    resArr.push(res);
                    //只有全部的都成功了,纔會返回resolve
                    if (i == arr.length - 1) {
                        return resolve(resArr);
                    }
                }, err => {
		    // 只要出錯就拋出
                    return reject(err)
                }).catch(err => {
                    console.log(err)
                })
            })(i)
        }
    })
}

//測試
const pro1 = new Promise((res,rej) => {
  setTimeout(() => {
    res('1')
  },1000)
})
const pro2 = new Promise((res,rej) => {
  setTimeout(() => {
    res('2')
  },2000)
})
const pro3 = new Promise((res,rej) => {
  setTimeout(() => {
    res('3')
  },3000)
})

const proAll = PromiseAll([pro1,pro2,pro3])
.then(res => 
  console.log(res) // 3秒以後打印 ["1", "2", "3"]
)
.catch((e) => {
  console.log(e)
})
複製代碼

版本二

function PromiseAll(promiseArrs) {
  return new Promise((resolve, reject) => { //返回一個新的Promise
    let arr = []; //定義一個空數組存放結果
    let i = 0;
    function handleData(index, data) { //處理數據函數
        arr[index] = data;
        i++;
        if (i === promiseArrs.length) { //當i等於傳遞的數組的長度時 
            resolve(arr); //執行resolve,並將結果放入
        }
    }
    for (let i = 0; i < promiseArrs.length; i++) { //循環遍歷數組
        promiseArrs[i].then((data) => {
            handleData(i, data); //將結果和索引傳入handleData函數
        }, reject)
    }
 })
}

// 測試
const pro1 = new Promise((res,rej) => {
  setTimeout(() => {
    res('1')
  },1000)
})
const pro2 = new Promise((res,rej) => {
  setTimeout(() => {
    res('2')
  },2000)
})
const pro3 = new Promise((res,rej) => {
  setTimeout(() => {
    res('3')
  },3000)
})

const proAll = PromiseAll([pro1,pro2,pro3])
.then(res => 
 console.log(res) // 3秒以後打印 ["1", "2", "3"]
)
.catch((e) => {
 console.log(e)
})
複製代碼

版本三

function PromiseAll(promiseArray) {
  return new Promise(function(resolve,reject){
    //判斷參數類型
    if(!Array.isArray(promiseArray)){
        return reject(new TypeError('arguments muse be an array'))
    }
    var counter = 0,
        promiseNum = promiseArray.length,
        resolvedArray = new Array(promiseNum);
    for(var i = 0; i < promiseNum; i++){
        (function(i){
            Promise.resolve(promiseArray[i]).then(function(value){
                counter++;
                resolvedArray[i] = value;
                if(counter == promiseNum){
                    return resolve(resolvedArray)
                }
            },function(reason){
                return reject(reason)
            })
        })(i)
    }
 })
}

// 測試
const pro1 = new Promise((res,rej) => {
  setTimeout(() => {
    res('1')
  },1000)
})
const pro2 = new Promise((res,rej) => {
  setTimeout(() => {
    res('2')
  },2000)
})
const pro3 = new Promise((res,rej) => {
  setTimeout(() => {
    res('3')
  },3000)
})

const proAll = PromiseAll([pro1,pro2,pro3])
.then(res => 
 console.log(res) // 3秒以後打印 ["1", "2", "3"]
)
.catch((e) => {
 console.log(e)
})
複製代碼

總結

無論版本如何變更,只要圍繞Promise.all幾個特色解讀就能夠併發

  1. Promise.all()接受一個 Array 類型的參數
  2. 只有數組中所有的 Promise 都變爲 resolve 的時候
  3. 返回一個新的 Promise 實例
  4. 只要有一個失敗,狀態就變成 rejected

更多關於Promise.all文章

介紹下 Promise.all 使用、原理實現及錯誤處理
手動實現一個 Promise.all()
實現promise.all方法
函數

關於我

相關文章
相關標籤/搜索