all方法 手写promise_实现Promise.allSettled

e254ff90d0dee8141aa54a367f7cc148.png

离上次写了 Promise.all 实现之后,已经隔了,呃,快一年了...

为什么又想起来写 Promise 的其他静态方法的实现呢?原因是最近已经连续两次收到了某跳动公司的面试邀请了。想着虽然不一定去面,但还是要把一些面试的八股文给熟悉一下,毕竟又写了快一年的业务代码了,自己的长进也是模模糊糊,好像也没有落到具体的东西上...

首先我们先回顾下 Promise.all 静态方法的实现

rencoo:[面试题]实现Promise.all​zhuanlan.zhihu.com
bc0327e9d68380fc0083929d715b40cb.png

记得当时写完之后,知乎上有个哥们 Rambo 还热心地指出我的问题,并且他自己也写了更加完美的实现

Rambo:Promise.all实现​zhuanlan.zhihu.com

接下来呢,我们就来看看现在要实现的 Promise.allSettled 静态方法

还是从它的用法入手:

相对于 Promise.all 需要所有 promise都成功时才 resolve或者有一个失败时即reject,Promise.allSettled 只关心所有 promise 是不是都被 settle 了,不管其是 rejected状态的 promise,还是非 rejected状态(即fulfilled)的 promise, 我都可以拿到它的最终状态并对其进行处理

Promise.allSettled 的结果数组中可能包含以下两种格式的数据

  • {status:"fulfilled", value:result} 对于成功的响应
  • {status:"rejected", reason:error} 对于 error

参照原理写的 Promise.all 的实现,我们根据 Promise.allSettled 的概念,稍作改变就可以写出实现

if (!Promise.allSettled) {
  Promise.allSettled = function (promises) {
    return new Promise(resolve => {
      const data = [], len = promises.length;
      let count = len;
      for (let i = 0; i < len; i += 1) {
        const promise = promises[i];
        promise.then(res => {
          data[i] = { status: 'fulfilled', value: res };
        }, error => {
          data[i] = { status: 'rejected', reason: error };
        }).finally(() => { // promise has been settled
          if (!--count) {
            resolve(data);
          }
        });
      }
    });
  }
}

测试下

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise2, promise1];

Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));

// expected output:
// "rejected"
// "fulfilled"

另外,更加简便的,我们也可以根据 Promise.all 来实现 Promise.allSettled

思路是让 Promise.all 入参中的所有 promise 都映射为新的最终状态为 fulfilled 的 promise (而新promise的result则根据原来promise的状态为fulfilled/reject来决定),这样就总是能让Promise.all 成功返回数组

if (!Promise.allSettled) {
  Promise.allSettled = function (promises) {
    return Promise.all(promises.map(p => Promise.resolve(p).then(res => {
      return { status: 'fulfilled', value: res }
    }, error => {
      return { status: 'rejected', reason: error }
    })));
  };
}

最后,再对之前 Promise.all 的实现做个修正!!! 当时的实现有误,对所有 promise 是否都被 settled 的判断是错误的,不能用结果的长度来判断。把原来的那几个测试 promise 顺序一颠倒就能看出问题来了,最后一个最快被 settled,按照长度来判定的话就 resolve 出去了,但这样是不对的,因为最后的那个 promise 最快 settled 时,前面几个慢的还没有被 settled,这时候时不能 resolve 最终结果的

function all(promises) {
    let len = promises.length, res = []
    // 修正
    let count = len
    if (len) {
        return new Promise(function (resolve, reject) {
            
            for(let i=0; i<len; i++) {
                let promise = promises[i];
                promise.then(response => {
                    res[i] = response

                    // 当返回结果为最后一个时
                    // 修正
                    // if (res.length === len) {
                    if (!--count) {
                        resolve(res)
                    }
                    
                }, error => {
                    reject(error)
                })
    
            }
        })
    }
}

// test
let p1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve(1)
        // reject(new Error('error 1'))
    }, 3000)
})

let p2 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve(2)
    }, 2000)
})

let p3 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve(3)
    }, 1000)
})

all([p1, p2, p3]).then(res => {
    console.log(res) //(*)
}).catch(console.log)
// 修正前*处 
// console:[empty × 2, 3]
// 修正后*处 
// console: [1, 2, 3]

最后想说的是,技术是件严谨的事情,我们需要对自己的输出有责任心,捂脸...

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值