彻底掌握 Promise - 原生 Promise.all 的实现(四)

32 篇文章 0 订阅
8 篇文章 0 订阅

  在 [ 彻底掌握 Promise - 原生 Promise 的实现(三) ] 中我们已经实现了简易版本的 Promise ,下面这篇文章,主要讲述 Promise.all 方法的实现。

        在 Promise 中内置了一些静态方法可以供我们更好的去处理异步操作,比如我们本篇文章要讲述的 Promise.all 静态方法的实现。

Promise.all

语法

         Promise 接收一个可迭代对象作为参数,也就是 Array、Set的数据结构,并且只返回一个Promise 的实例,返回结果是一个数组,数组包含了所有的回调结果。

说明

      此方法在集合多个 promise 的返回结果时很有用。

         完成(Fulfillment):
            如果传入的可迭代对象为空,Promise.all 会同步地返回一个已完成(resolved)状态的promise。如果所有传入的 promise 都变为完成状态,或者传入的可迭代对象内没有 promisePromise.all 返回的 promise 异步地变为完成。在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise 值)。

         失败/拒绝(Rejection):
             如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成。

示例

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

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]

          返回结果

                通过返回值我们可以发现,返回值是一个数组,数组内包含了我们传入数据的所有结果。

原生实现

    通过逆解析的思路去考虑原生实现的话,有下面几个步骤

  1. 返回值可以调用 then 方法,所以我们要返回一个 Promise 对象
  2. 在所有值都执行完,或者有一个为失败,就立即返回相应状态
class iPromise {
    // 通过 static 的关键字可以创建类的静态方法
  static all(arr) {
    // 创建一个保存所有结果的list
    let resultList = []
    const len = arr.length
    // 返回一个 Promise 对象,让外部可以调用 then 方法获取结果
    return new iPromise((resolve, reject) => {
      // 保存结果
      function addData(key, value) {
        resultList[key] = value
      }
      // 循环执行传入的回调函数获取返回结果
      for (let i = 0; i < len; i++) {
        // 判断当前项,是否为一个 Promise 对象, 如果是, 调用它的then方法获取返回结果
        if (arr[i] instanceof iPromise) {
            // 执行这个 Promise 对象并获取返回结果
          arr[i].then(value => addData(i, value), reason => reject(reason))
        } else {
          // 是普通对象的话, 直接返回结果
          addData(i, arr[i])
        }
      }
        resolve(resultList)
    })
  }
}

        验证实现逻辑

let promise1 = new iPromise((resolve, reject) => {
  resolve('promise的结果')
})
// 调用 iPromise.all 的静态方法
iPromise.all([1, promise1, '100']).then(value => {
  console.log(value); // [1, 'promise的结果', '100']
}, reason => {
  console.log(reason);
})

        通过代码的返回结果可以看到,我们的实现逻辑也是没有问题的。但是如果涉及到异步操作的话,它还可以正常返回并且拿到结果吗?

含有异步操作的

let promise1 = new iPromise((resolve, reject) => {
  setTimeout(() => {
    console.log('延时器执行');
    resolve(12)
  }, 1000)

  // resolve(12)
})
iPromise.all([1, promise1, '100']).then(value => {
  console.log(value); // ??
}, reason => {
  console.log(reason);
})

执行结果 

 

        从执行结果可以看出来,它并没有拿到延时器执行的结果,也就是说在 for 循环的内部没有等延时器执行,在循环完成后就直接返回了结果。所以在异步操作的位置获取到一个空。下面我们要在原有实现的基础上去兼容处理异步操作。

  1. 首先声明一个计数器用来记录每一项是否全部执行完成
  2. 在计数器的大小等于传入数组的 length 时,调用 resolve 方法返回结果
class iPromise {
    // 通过 static 的关键字可以创建类的静态方法
  static all(arr) {
    // 创建一个保存所有结果的list
    let resultList = []
    const len = arr.length
    let index = 0 // 计数器
    // 返回一个 Promise 对象,让外部可以调用 then 方法获取结果
    return new iPromise((resolve, reject) => {
      // 保存结果
      function addData(key, value) {
        resultList[key] = value
        // 在每次保存结果的时候都让计数器自增
        index++
        // 判断当前计数器的长度, 是否等于传入数据的长度
        // 如果等于则表示数组内的所有结果都已经获取到了, 调用 resolve 方法返回结果
        if (index == len) {
          resolve(resultList)
        }
      }
      // 循环执行传入的回调函数获取返回结果
      for (let i = 0; i < len; i++) {
        // 判断当前项,是否为一个 Promise 对象, 如果是, 调用它的then方法获取返回结果
        if (arr[i] instanceof iPromise) {
            // 执行这个 Promise 对象并获取返回结果
          arr[i].then(value => addData(i, value), reason => reject(reason))
        } else {
          // 是普通对象的话, 直接返回结果
          addData(i, arr[i])
        }
      }
    })
  }
}

验证逻辑

let promise1 = new iPromise((resolve, reject) => {
  setTimeout(() => {
    console.log('1s的延时器执行了');
    resolve('异步的Promise')
  }, 1000)

})
iPromise.all([1, promise1, '100']).then(value => {
  console.log(value);
}, reason => {
  console.log(reason);
})

返回结果

         调用我们改写后的 all 方法可以看到,all 方法中是先执行的延时器的调用,再返回的结果,而且全部的结果页都已经获取到了。

完整版本

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class iPromise {

  constructor(callback) {
    // 使用 try,catch 的方式进行函数执行的异常捕获, 放置代码的阻塞
    try {
      callback(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  value = null
  status = PENDING
  reason = null
  fulfilledList = []
  rejectedList = []

  resolve = (value) => {
    if (this.status != PENDING) {
      return
    }
    this.status = FULFILLED
    this.value = value
    while (this.fulfilledList.length) {
      this.fulfilledList.shift()()
    }
  }

  reject = (reason) => {
    if (this.status != PENDING) {
      return
    }
    this.status = REJECTED
    this.reason = reason
    while (this.rejectedList.length) {
      this.rejectedList.shift()()
    }
  }

  then(successCallback, errorCallback) {
    successCallback = successCallback ? successCallback : value => value
    errorCallback = errorCallback ? errorCallback : reason => reason
    return new iPromise((resolve, reject) => {
      if (this.status == FULFILLED) {
        // 增加一个 setTimeout 的原因是把当前任务放到下一个宏任务里去执行
        // 因为处理异步可能会有问题
        setTimeout(() => {
          try {
            let result = successCallback(this.value)
            isiPromise(result, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else if (this.status == REJECTED) {
        setTimeout(() => {
          try {
            let result = errorCallback(this.value)
            isiPromise(result, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else {
        this.fulfilledList.push(() => {
          setTimeout(() => {
            try {
              let result = successCallback(this.value)
              isiPromise(result, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
        this.rejectedList.push(() => {
          setTimeout(() => {
            try {
              let result = errorCallback(this.value)
              isiPromise(result, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
      }
    })
  }

  static all(arr) {
    // 创建一个保存所有结果的list
    let resultList = []
    const len = arr.length
    let index = 0
    // 返回一个 Promise 对象,让外部可以调用 then 方法获取结果
    return new iPromise((resolve, reject) => {
      // 保存结果
      function addData(key, value) {
        resultList[key] = value
        index++
        if (index == len) {
          resolve(resultList)
        }
      }
      // 循环执行传入的回调函数获取返回结果
      for (let i = 0; i < len; i++) {
        // 判断当前项,是否为一个 Promise 对象, 如果是, 调用它的then方法获取返回结果
        if (arr[i] instanceof iPromise) {
          arr[i].then(value => addData(i, value), reason => reject(reason))
        } else {
          // 是普通对象的话, 直接返回结果
          addData(i, arr[i])
        }
      }
    })
  }
}


function isiPromise(result, resolve, reject) {
  if (result instanceof iPromise) {
    result.then(resolve, reject)
  } else {
    resolve(result)
  }
}

如有问题,敬请留言说明 ~ 

        到这里本章节的 Promise.all 的静态方法已经可以实现了,下一章节,我们会逐步去实现 Promise 的其他静态方法
        实现 Promise.finally 的静态方法
        实现 Promise.resolve 的静态方法
        实现 Promise.catch 的静态方法

        ...

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值