面试必备--手写Promise.all与.race

最近面试被问到了手写Promise .allPromise.race,奈何没有自己实现过,只能阿巴阿巴
面完之后,冷静下来思考了该如何实现,并把他写了下来(在实现过程中确实收获不少,让我对这两个方法有了更深的理解)

这里用到了上次写得promiseAjax,用于测试

function promiseAjax(url, type, data, timeout = 8000) {
    return new Promise((resolve, reject) => {
        type = type.toLocaleLowerCase()
        const xhr = new XMLHttpRequest()
        xhr.timeout = timeout
        if (type === 'get') {
            if (data) {
                url += '?'
                for (let key in data) {
                    url += (key + '=' + data[key] + '&')
                }
                url = url.substring(0, url.length - 1)
            }
            xhr.open(type, url)
            xhr.send(null)
        }

        if (type === 'post') {
            xhr.open(type, url)
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            if (data) {
                xhr.send(data)
            } else {
                xhr.send(null)
            }
        }

        let timer = setTimeout(() => {
            reject('请求超时了')
            throw new Error('请求超时')
        }, timeout)
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    let result = JSON.parse(xhr.responseText)
                    clearTimeout(timer)
                    resolve(result)
                }
            }
        }

        xhr.onerror = function(err) {
            reject('请求失败,' + err)
        }
    })
}

好了,接下来是实现代码及过程

第一步:函数基本结构
const MyPromise = (function() {
      const _promise = {
          all: _all,
          race: _race,
      }
      
      function _all(promiseArr) {

      }

      function _race(promiseArr) {
         
      }
		
	// 判断一个对象是否是Promise
      function isPromise(p) {
          return !!p && (typeof p === 'object' || typeof p === 'function') && typeof p.then === 'function'
      }

      return _promise
 })()
实现_all方法

Promise.all功能

  1. 它接受一个数组作为参数。
  2. 数组可以是Promise对象,也可以是其它值,只有Promise会等待状态改变。
  3. 当所有的子Promise都完成,该Promise完成,返回值是全部值的数组。
  4. 如果有任何一个失败,该Promise失败,返回值是第一个失败的Promise的结果。
    参考来源

主要思路:

  1. 新建一个Promise实例(明确返回的结果是一个Promise)
  2. 新建一个用于保存回调结果的数组resultArr
  3. 遍历传入的Promise数组
  4. 判断当前项是否为Promise类型,若不是,直接将结果存入resultArr(这里需要传入一个当前项下标,用于按传入的数组位置排序)
  5. 若是,则使用.then取得回调结果的值,若resultArr的长度等于传入的Promise数组的长度,则resolve(resultArr)
  6. 使用.catch回调取得失败的值,然后直接reject
/**
  * 实现功能:传入的Promise数组所有状态都为Fulfilled时,返回所有Promise的结果数组,
  * 若有一个状态为Reject,则提前reject
  * */
 function _all(promiseArr) {
 	// 新建一个Promise实例(明确返回的结果是一个Promise)
     return new Promise((resolve, reject) => {
         // 保存结果数组
         let resultArr = []
             // 循环遍历每一个Promise
         promiseArr.forEach((promiseItem, index) => {
                 // 判断是否是Promise
                 if (isPromise(promiseItem)) {
                     // 如果Promise成功
                     promiseItem.then(result => {
                             pushSuccessResult(result, index)
                         })
                         // 当有一个Promise失败则直接reject
                         .catch(err => {
                             reject(err)
                         })
                 } else {
                 	// 如果当前项不是Promise对象,直接存入
                     pushSuccessResult(promiseItem, index)
                 }
             })
             // 存入结果数组
         function pushSuccessResult(result, index) {
             resultArr.push({
                 index,
                 result
             })
             if (resultArr.length === promiseArr.length) {
                 // 按传入的数组位置进行排序
                 resultArr.sort((a, b) => a.index - b.index)
                 resolve(resultArr.map(item => item.result))
             }
         }
     })
 }

这里需要注意的是保持 返回的结果数组中各项的位置传入数组各项的位置 一致

实现_race方法

只要有一个Promise触发回调,则resolve或reject

主要思路:

  1. 新建一个Promise实例(明确返回的结果是一个Promise)
  2. 遍历传入的Promise数组
  3. 判断当前项是不是Promise,若不是,则直接resolve该项
  4. 若是,取得该项的回调,返回结果
// 实现功能:只要有一个Promise触发回调,则resolve或reject
function _race(promiseArr) {
     return new Promise((resolve, reject) => {
        promiseArr.forEach(promiseItem => {
             if (isPromise(promiseItem)) {
                 promiseItem.then(result => {
                         resolve(result)
                     })
                     .catch(err => {
                         reject(err)
                     })
             } else {
                 resolve(promiseItem)
             }
         })
     })
  }
测试
	  let posts = promiseAjax('http://jsonplaceholder.typicode.com/posts', 'get', null)

      let comments = promiseAjax('http://jsonplaceholder.typicode.com/comments', 'get', null)

      let albums = promiseAjax('http://jsonplaceholder.typicode.com/albums', 'get', null)

      MyPromise.all([3, comments, posts], 2000).then(res => {
          console.log(res);
      }).catch(err => {
          console.log(err);
      })

      MyPromise.race([albums, comments]).then(res => {
              console.log(res)
          })
          .catch(err => {
              console.log(err);
          })

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值