前言

面试中让实现一个Promise.all方法,当时没有什么头绪,网上部分博客中的实现感觉也有些问题,所以参考他人的实现,然后加入一些自己的想法。

Promise.all的原理

Promise.all(iterable)方法接收一个可迭代对象作为参数,比如ArrayString,如果参数中所有的promise都完成(resolved)或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败 promise 的结果。

使用

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});

要点

  • 接收一个可迭代对象iterable作为参数
  • 返回一个Promise对象
  • 参数中所有的promise都完成(resolved)或参数中不包含 promise 时回调完成(resolve)
  • 如果参数中 promise 有一个失败(rejected),此实例回调失败(reject)

实现代码

Promise.myAll = function(iterable) {
    return new Promise((resolve, reject) => {
        // 判断是否可迭代
        if(typeof iterable[Symbol.iterator] !== "function") {
            reject(`${iterable} is not iterable`);
        }
        let promisesLength = iterable.length;
        // 长度为0时返回空数组
        if(promisesLength === 0) {
            resolve([]);
        }
        let resultArr = new Array(promisesLength);
        let count = 0;
        for(let [index, proItem] of Object.entries(iterable)) {
            Promise.resolve(proItem).then(res => {
                // 将结果放在对应位置
                resultArr[index] = res;
                count ++;
                // 完成数量等于总数时,返回成功结果
                if(count === promisesLength) {
                    resolve(resultArr);
                }
            }).catch(err => {
                // 异常时立刻返回错误结果
                reject(err);
            })
        }
    })
}

测试

  • 参数为字符串时,输出结果相同

    image-20201014142009494

  • 参数为Promise数组,且全部resolved

    image-20201014142524219

  • 参数为Promise数组,且有reject

    image-20201014142703195

拓展

依法炮制,实现一下其它几个Promise方法

allSettled

Promise.myAllSettled = function(iterable) {
    return new Promise((resolve, reject) => {
        if(typeof iterable[Symbol.iterator] !== "function") {
            reject(`${iterable} is not iterable`);
        }
        let promisesLength = iterable.length;
        if(promisesLength === 0) {
            resolve([]);
        }
        let count = 0;
        let resultArr = new Array(promisesLength);
        for(let [index, proItem] of Object.entries(iterable)) {
            Promise.resolve(proItem).then(res => {
                resultArr[index] = {
                    status: "fulfilled", 
                    value: res
                }
            }).catch(err => {
                resultArr[index] = {
                    status: "rejected", 
                    value: err
                }
            }).finally(() => {
                count ++;
                if(count === promisesLength) {
                    resolve(resultArr);
                }
            })
        }
    })
}

race

Promise.myRace = function(iterable) {
    return new Promise((resolve, reject) => {
        if(typeof iterable[Symbol.iterator] !== "function") {
            reject(`${iterable} is not iterable`);
        }
        let promisesLength = iterable.length;
        if(promisesLength === 0) {
            resolve([]);
        }
        for(let proItem of iterable) {
            Promise.resolve(proItem).then(res=> {
                resolve(res);
            }).catch(err => {
                reject(err);
            }) 
        }
    })
}

any

Promise.myAny = function(iterable) {
    return new Promise((resolve, reject) => {
        if(typeof iterable[Symbol.iterator] !== "function") {
            reject(`${iterable} is not iterable`);
        }
        let promisesLength = iterable.length;
        if(promisesLength === 0) {
            reject(undefined);
        }
        let resultArr = new Array(promisesLength);
        let count = 0;
        for(let [index, proItem] of Object.entries(iterable)) {
            Promise.resolve(proItem).then(res => {
                resolve(res);
            }).catch(err => {
                resultArr[index] = err;
                count ++;
                if(count === promisesLength) {
                    reject(resultArr);
                }
            })
        }
    })
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

node.js异步编程 上一篇
Vue的MVVM响应式原理 下一篇