记一次在面试过程中遇到的 promise 编程题

816 阅读3分钟

昨天面试的时候遇到了这两个函数编程题,但是该死的是完全没有思路(悲伤)

  1. 利用 Promise 完成一个队列,队列中的任务数满了的话,后续任务不执行,当队列中的任务有完成的状态,才会添加新的任务进入队列

  2. 完成一个 retry 函数,传入一个执行函数和一个计数器,在一定次数范围内,如果函数执行失败的话会再次尝试执行该函数,直到执行成功

面试结束之后,我上网上搜索了一下类似的问题,接下来是我总结之后的思路,以及最后成功的代码

Promise 队列

这个问题有以下几个重点

  • 队列: 存放未执行的任务队列
  • 计数器: 存放当前正在执行的任务数量
  • 任务执行最大数量
  • 添加任务函数
  • 执行任务函数

接下根据我们上述描述的重点来创建一个任务队列执行类

  class Scheduler {
    // 初始化任务队列,以及根据传入的数量来限制最大执行数量
    constructor(maxCount) {
      this.maxCount = count
      this.list = []
      this.count = 0
    }
    add() {

    }
    start() {

    }
  }

接下我们就要来编写添加函数了,在写添加函数之前我们首先思考一下这个添加函数里面应该注意那些东西

首先,如果我们想要在任务执行完成之后对执行结果操作的话,这个 add 函数必须返回一个 promise 函数

  add(task) {
    return new Promise(resolve => {
      resolve()
    })
  }

添加一个任务的时候,我们需要把这个任务添加到未执行的任务队列的存放起来

  add(task) {
    return new Promise(resolve => {
      resolve(() => {
        this.list.push(task())
      })
    })
  }

这个时候就出现了一个问题,我们应该怎样监控这个任务是否已经执行完成,这个时候 promise 的作用就来了,我们在 then 中就可以判断到函数是否执行成功

  add(task) {
    return new Promise(resolve => {
      resolve(() => {
        this.list.push(Promise.resolve(task()).then())
        // 如果 task 执行之后的返回值是一个promise对象则可以修改成以下形式
        // this.list.push(task().then())
      })
    })
  }

添加函数先写到这里,然后我们来写执行函数,在start函数中我们需要做的就是判断一下正在执行任务数是否小于最大执行数,然后从待执行队列中取出一个任务并执行,然后将正在执行的任务数加上1

  start() {
    if(this.count < this.maxCount) {
      this.count++
      this.list[0] && this.list.shift()()
    }
  }

接下来修改 add 函数 为当我们添加一个任务时,在最后执行一下执行函数,在执行任务完成之后,减少执行队列数,并触发执行函数,最后把任务的返回值 return 出来

  add(task) {
    return new Promise(resolve => {
      this.list.push(() => {
        resolve(Promise.resolve(task()).then(s => {
          this.count--
          this.start()
          return s
        }))
      })
      this.start()
    })
  }

接下来实例化任务队列,并创建一个异步执行方法和一个添加任务函数

  let scheduler = new Scheduler(5)
  let timeout = (time) => {
    return new Promise(resolve => {
      setTimeout(resolve, time)
    })
  }

  let addTask = function addTask(time, order) {
    scheduler.add(() => timeout(time)).then(() => {
      console.log(time, order)
    })
  }

附上完整代码

class Scheduler {
constructor(count) {
  this.list = []
  this.maxCount = count
  this.count = 0
}
add(task) {
  return new Promise(resolve => {
    this.list.push(() => {
      resolve(Promise.resolve(task()).then(s => {
        this.count--
        this.start()
        return s
      }))
    })
    this.start()
  })
}
start() {
  console.log(this.count)
  if(this.count < this.maxCount) {
    this.count++
    this.list[0] && this.list[0]()
    this.list.shift()
  }
}
}

let scheduler = new Scheduler(5)
let timeout = (time) => {
return new Promise(resolve => {
  setTimeout(resolve, time)
})
}

let addTask = function addTask(time, order) {
scheduler.add(() => timeout(time)).then(() => {
  console.log(time, order)
})
}

addTask(5000,'1')
addTask(1100,'2')
addTask(2200,'3')
addTask(3300,'4')
addTask(3300,'5')
addTask(3300,'6')
addTask(3300,'7')
addTask(2200,'8')

retry 函数

function retry(fun, count) {
  if(count === 0) return
  let val = fun()
  if(val > 3) {
    return val
  } else {
    return retry(fun, --count)
  }
}

function random() {
  return Math.random() * 5
}
console.log(retry(random, 5))

这个问题现在看起来挺简单的,但是当时估计相差了,思路都理解错了,难受