手写Promise.all

有些面试官脑洞很大,非要让面试者手写一下Promise.all的实现代码,就很离谱

算了,没有办法,谁让我是打工人,写吧。

  • 先准备两个函数用于测试所用。(完整代码在最后)
    function fun1 () {
     return new Promise((res, rej) => {
       setTimeout(() => {
         res(1)
       }, 1000);
     })
   }
   function fun2 () {
     return new Promise((res, rej) => {
       setTimeout(() => {
         res(2)
       }, 10);
     })
   }
  • 先聊一个题外话,关于Promise是异步的还是同步的?这个问题也会经常问到,直接说我的答案,就不写代码来证明了,毕竟这篇文章主要是说手写的问题。
    Promise本身属于同步的,但是.then和catch和finally方法是异步的
    Promise.all属于异步方法
  • 手写Promise.all

在手写之前先要知道它的作用,Promise.all是一个方法,接收一个数组,数组中是一个个的Promise实例对象,Promise.all会等待所以Promise实例都返回成功后在返回成功结果是一个数组(迭代器),如果有一个失败则返回失败。看一段示例代码。

// 输出 [1,2]
   Promise.all([fun1(),fun2()]).then(res=>{
     console.log(res);
   }).catch(err=>console.log(err)).finally()

然后在Promise构造函数上添加一个方法来实现自定义的.all方法,代码如下。

Promise.myall = function (pmarr) {
  // 手写Promise.all最终返回的还是一个promise,所以我们创建一个promise并返回
  return new Promise((resolve, reject) => {
    try {
      /**
     * 首先要判断迭代器的个数
     * 但是这里不能用length的方式去判断,因为它可能不是一个数组,也限定必须是数组
     * 要用循环的方式去判断,一定要用forin 因为它不一定是数组可能是对象
     * 
     * 如果你考虑严谨一点的话,可以对pmarr进行一些校验 如下
    */
      if (pmarr === undefined) reject('没有迭代器')
      let length = 0 // 记录迭代器的个数
      let resultLength = 0// 记录已执行的promise个数
      let result = [] // 用于存储返回暑假
      for (const key in pmarr) {
        length++
        /**
         * 1因为item不一定都是promise对象,所以要全部转一下,
         * 2因为每个promise成功或者是失败都有回调,所以我们要去拿到每一个prmoise的返回状态
         * 3拿到每一个prmise的返回状态后不能用push的方式添加到result中,返回结果的顺序需要和pmarr的顺序一致
         * 4当其中有一项prmise返回的失败则整个promise.all就会返回失败,所以直接reject放在第二个参数中,
         *    只要失败一个并返回失败终止循环
         * 5判断迭代器是否都执行完,全部执行完后返回成功
        */
        Promise.resolve(pmarr[key]).then(res => {
          result[key] = res
          resultLength++
          if (length === resultLength) resolve(result)
        }, reject)
      }
      // 当没有转入迭代器时直接返回成功状态
      if (!length) resolve([])
    } catch (error) {
      reject(error)
    }
  })
}

// 输出[1,2]
Promise.myall([fun1(),fun2()]).then(res=>{
  console.log(res);
})

在测试一下其它情况的结果。因为在myall方法中使用的是forin来遍历的,所以大部分情况都会返回成功状态。可能并不严谨。

// 输出 []
Promise.myall([]).then(res=>{
  console.log(res);
})

// 输出 [1, 2, 3]
Promise.myall([1,2,3]).then(res=>{
  console.log(res);
})

// 输出 [a: 1, b: 2] 当然原生的promise.all是不支持传入对象的,会抛出错误,
// 如果我们把上面的forin换为其它遍历方式加一个trycatch,打印出错误也是一样的
Promise.myall({a:1,b:2}).then(res=>{
  console.log(res);
})
// 输出 []
Promise.myall(null).then(res => {
  console.log(res);
}).catch(err => console.log(err))

内容就这些了,如果哪里说的不对或者写的不对,欢迎喷我,你的打击是我成长的动力,嘿嘿!最后在附上所有代码,方便直接CV。点赞不迷路。
function fun1 () {
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(1)
    }, 1000);
  })
}
function fun2 () {
  return new Promise((res, rej) => {
    setTimeout(() => {
      res(2)
    }, 10);
  })
}
/**
 * Promise本身属于同步的,但是.then和catch和finally方法是异步的
 * Promise.all属于异步方法
 * 
*/
// 输出 [1,2]
// Promise.all([fun1(), fun2()]).then(res => {
//   console.log(res);
// }).catch(err => console.log(err)).finally()


/**
 * 手写Promise.all
 * 在手写之前先要知道它的作用,
 * Promise.all是一个方法,接收一个数组,数组中是一个个的Promise实例对象,
 * Promise.all会等待所以Promise实例都返回成功后在返回成功结果是一个数组(迭代器),如果有一个失败则返回失败
*/

Promise.myall = function (pmarr) {
  // 手写Promise.all最终返回的还是一个promise,所以我们创建一个promise并返回
  return new Promise((resolve, reject) => {
    try {
      /**
     * 首先要判断迭代器的个数
     * 但是这里不能用length的方式去判断,因为它可能不是一个数组,也限定必须是数组
     * 要用循环的方式去判断,一定要用forin 因为它不一定是数组可能是对象
     * 
     * 如果你考虑严谨一点的话,可以对pmarr进行一些校验 如下
    */
      if (pmarr === undefined) reject('没有迭代器')
      let length = 0 // 记录迭代器的个数
      let resultLength = 0// 记录已执行的promise个数
      let result = [] // 用于存储返回暑假
      for (const key in pmarr) {
        length++
        /**
         * 1因为item不一定都是promise对象,所以要全部转一下,
         * 2因为每个promise成功或者是失败都有回调,所以我们要去拿到每一个prmoise的返回状态
         * 3拿到每一个prmise的返回状态后不能用push的方式添加到result中,返回结果的顺序需要和pmarr的顺序一致
         * 4当其中有一项prmise返回的失败则整个promise.all就会返回失败,所以直接reject放在第二个参数中,
         *    只要失败一个并返回失败终止循环
         * 5判断迭代器是否都执行完,全部执行完后返回成功
        */
        Promise.resolve(pmarr[key]).then(res => {
          result[key] = res
          resultLength++
          if (length === resultLength) resolve(result)
        }, reject)
      }
      // 当没有转入迭代器时直接返回成功状态
      if (!length) resolve([])
    } catch (error) {
      reject(error)
    }
  })
}

// 输出[1,2]
Promise.myall([fun1(),fun2()]).then(res=>{
  console.log(res);
})

// 输出 []
// Promise.myall([]).then(res=>{
//   console.log(res);
// })

// 输出 [1, 2, 3]
// Promise.myall([1,2,3]).then(res=>{
//   console.log(res);
// })

// 输出 [a: 1, b: 2] 当然原生的promise.all是不支持传入对象的,会抛出错误,
// 如果我们把上面的forin换为其它遍历方式加一个trycatch,打印出错误也是一样的
// Promise.myall({a:1,b:2}).then(res=>{
//   console.log(res);
// })
// 输出 []
// Promise.myall(null).then(res => {
//   console.log(res);
// }).catch(err => console.log(err))

你可能感兴趣的:(js,javascript,前端,vue.js)