Promise常见面试题

138 阅读2分钟

一、promise.all

传的是一个数组, 返回一个promise对象, 如果执行的promise都成功的话 返回 成功的结果数组 否则 返回失败的结果

function PromiseAll(pArr) {
  if (!Array.isArray(pArr)) {
    return `请传入一个数组`;
  }
  // 返回一个Promise对象
  return new Promise((resolve, reject) => {
    // 用于存储全部成功的结果
    let res = [];
    // 当前promise执行数
    let count = 0;
    for (let i = 0; i < pArr.length; i++) {
      // 防止传入数字或字符串的情况
      Promise.resolve(pArr[i])
        .then((r) => {
          // 若成功的count+1
          count++
          // res[i]为当前promise处理后的结果, 注意这里不能用push 否则不能对应到当前的promise
          res[i] = r
          // 如果count 等于 promise全部的长度
          if(count === pArr.length) {
            resolve(res)
          }
        })
        .catch((err) => {
          reject(err)
        });
    }
  });
}


二、promiseAllSettle

传的是一个数组, 返回一个promise对象, 无论成功或失败都向数组里存入当前的值 例如:{status: fulffilled, value: '123'}, 和promise.All类似

function promiseAllSettle(pArr) {
  if(!Array.isArray(pArr)) {
    return `传入的参数必须是数组`
  }
  return new Promise((resolve, reject) => {
    let count = 0
    let res = []
    for(let i = 0;i < pArr.length;i++) {
      Promise.resolve(pArr[i]).then(r => {
        res[i] = {
          // 成功状态
          status: 'fullfilled',
          value: r
        }
        count++
      }).catch(err => {
        res[i] = {
          // 失败状态
          status: 'rejected',
          value: err
        }
        count++        
      }).finally(() => {
        // 如果执行完了 返回结果
        if(count === pArr.length) {
          resolve(res)
        }
      })
    }
  })
}

let p1 = new Promise((resolve, reject) => {
  resolve('p1')
})

let p2 = new Promise((resolve, reject) => {
  reject('p2')
})

let p3 = new Promise((resolve, reject) => {
  reject(`p3`)
})

let pArr = [p1, p2, p3]

let arr = promiseAllSettle(pArr)

三、promiseFinally

 Promise.prototype.promiseFinally = function(cb) {
  return this.then((value) => {
      // 执行一次传入的回调函数 在返回原始的promise值
      return Promise.resolve(cb()).then(() => {
        return value
      })
  }, (err) => {
    return Promise.resolve(cb()).then(() => {
      throw err
    })
  })
}

let p = new Promise((resolve, reject) => {
  reject(`error1`)
}).promiseFinally(() => {
  console.log(123) 
})

console.log(p)

四、promise并发

// 定义url数组
const urls = [{
  info: 'link1',
  time: 3000
}, {
  info: 'link2',
  time: 2500
}, {
  info: 'link3',
  time: 1600
}, {
  info: 'link4',
  time: 1000
}, {
  info: 'link5',
  time: 2000
}]

// 解析url返回promise
function loadImg(url) {
  return new Promise((resolve, reject) => {
    console.log(`---start`, url.info)
    setTimeout(() => {
      resolve(url.info)
      console.log('---end', url.info)
    }, url.time)
  })
}

class PromiseQueue {
  constructor(options = {}) {
    // 最大并发数量
    this.concurreny = options.concurreny || 3
    // 当前并发数量
    this.concurrent = 0
    // 存储任务数组
    this.pendingList = []
  }
  add(task) {
    // 向数组推动任务
    this.pendingList.push(task)
    // 执行任务
    this.run()
  }
  run() {
    // 如果当前并发数量大于等于最大并发数量, 就不往下执行了 比如最大并发数量为3 就是 0 1 2 进任务 到3了停止 
    // 等0 1 2 其中一个结束了 此时concurrent = 2 满足条件 在执行下一个任务 
    if(this.concurrent >= this.concurreny || this.pendingList.length === 0) {
      return 
    }
    // 使当前并发数+1
    this.concurrent++
    // 取出
    let {fn} = this.pendingList.shift()
    fn().then(res => {
      this.concurrent--
      this.run()
    })
  }
}

const p1 = new PromiseQueue()

urls.forEach(item => {
  p1.add({
    fn: () => loadImg(item),
  })
})

let HighTask = {
  info: 'link111',
  time: 3000
}

p1.add({
  fn: () => loadImg(HighTask),
})