[面试] 考验你对 Promise 的熟度之进阶应用题

大厂技术  高级前端  Node进阶

点击上方 程序员成长指北,关注公众号

回复1,加入高级Node交流群

这一篇应该早在两年前就要写了,因为是当初面试某间知名的 Udxxx 线上课程的面试题,现在只能用我薄弱的记忆回忆一下。要说进阶题是因为当初我自认为对 Promise 算了解,但遇到这种应用题还是被 KO。

55f138e63ad86066d8c44c264941f39f.png

有碰过 Promise 的人可能会觉得也没什么难的,就是处理非同步 async

var p = num => new Promise((resolve, reject) => {
    if(num <= 2) {
        setTimeout(resolve('Success!'), Math.random()*1000);
        return;
    }
    reject('Failure!') 
});

p(1)
  .then(res => { console.log(res)})
  .catch( error => { console.log(error)}); 
// "Success!"

但除了比较常见的 fetch API,其实还有很多进阶应用呢!接下来这题就要考考大家对 Promise 的熟度,因为当初的我是真的完全不知道怎麽解啊…

来看题目吧

总共 10 个 tasks 一次 call 最多 3 个,每个 task 需要完成的时间都不同。

34e3638eac75fc9d845eaf344873d69b.png

请使用以下提供的范本,写出能完成这 10 个任务的方法。

/**
 * @return { promise }
 * 写一个执行 task 的 function
 */
function task(){}
class handleTask{
  constructor(maxCount){
    this.maxCount = maxCount;
    this.pendingTask = [];
    this.completed = 0; 
  }
  run(tasks){}
}

依据题目与提供的函数先推断可能需要哪些变量:

  • totalTask: number = 10 → 总共几个 task

  • maxTask: number = 3 → 一次最多执行几个 task

  • pendingTask: [number] = [promise, promise...] → 等待被执行的 task

  • completed:number → Track 成功了几个 Tasks

写一个 promise 的 task 还算简单,

/**
 * @return { promise }
 * 写一个执行 task 的 function
 */
function task(){
  return new Promise((resolve, reject) => {
      console.log('running')
      setTimeout(resolve(), Math.random()*1000) // 每个 task 所需时间不同,所以这边用 random
  }).then(() => {
      console.log('done')
  }).catch(() => {
      console.log('error')
  })
}

但要写 run() 这个 method 时我完全卡住:

  • 不知道怎麽一次执行三次,是 run 3 次 task 吗?

  • 那执行完要怎麽在执行别的 task 直到执行完 ?

count

总觉得会但又不知道怎麽解,其实需要一个很重要的东西 count ,来计算你现在到底在执行哪一个 task

count→计算现在正在执行的 Task,若 count < 3 就会执行 task(),所以一开始会先执行 3 个 Tasks。

当其中一个 task 做完, count 就会 -1 ,然后继续做下一个 task

run(tasks){
      if(this.count < this.maxCount){
        this.count ++;
        tasks().then(() => {
          this.count --;
        })
      } 
}
pendingTask

另一个重点就是 pendingTask 拿来存待处理的任务,所以一开始会是 [4,5,6,7,8,9,10] ,当 [1,2,3] 其中一个 task 先处理完就会抓 task 4 继续处理,pendingTask 也会变 [5,6,7,8,9,10] 如下图:

4e037cea00ebc084cc0350d49ca719e8.png

run(tasks){
      if(this.count < this.maxCount){
        this.count ++;
        tasks().then(() => {
          this.count --;
          this.pendingTask.shift()
        })
      } else{
          this.pendingTask.push(tasks);
      }
}
class handleTask{
  constructor(maxCount){
    this.maxCount = maxCount; // 3
    this.count = 0;  
    this.pendingTask = [];
    this.completed = 0;
  }

  run(tasks){
    if(this.count < this.maxCount){
        this.count ++;
        tasks().then(() => {
          this.count --;
          this.completed ++;
          this.pendingTask.shift(0);
          
          console.log('completed: ', this.completed)
        })
      } else{
        this.pendingTask.push(tasks);
      }
  }
}

function task () {
  //... 略
}

let myTask = new handleTask(3);

for(let i=0; i< 10; i++){
  myTask.run(task);
}

出现问题: 总共才完成 3 个任务

But, 发现 completed 最后才等于 3 而已,代表只成功完成 3 个 tasks!一定是哪裡出问题了啊。

解決

这是因为 myTask.run(task); 会一瞬间跑十次 (不管 task 有没有做完), pendingTask一开始会是 [4,5,6,7,8,9,10] ,然后因为

if(this.count < this.maxCount){
    this.count ++;
    //  前三个 task 会陆续做完      
    tasks().then(() => {
        this.count --;
        this.pendingTask.shift(0);
    })
}

真正只有 [1, 2, 3] 会跑进 if 裡面 run task,而 pendingTask 虽然会是 [7, 8, 9, 10] ,但其实 [4, 5, 6] 都没有被执行 run:

cc84920375ba4ef290520fa7d9526235.png

为了继续执行剩下任务,应该要把 this.pendingTask.shift(0); ,改成

if(this.pendingTask.length > 0) this.run(this.pendingTask.shift())

这时候蛮考验 JS 基本功的,若 this.pendingTask[4,5,6,7,8,9,10] ,那this.pendingTask.shift() 会回传,4 ; this.pendingTask 则变 [5,6,7,8,9,10] ,也就是把任务 4 继续丢尽 run 裡跑这样才能把所有任务都完成。


作者:HannahLin   来源:medium 原文 https://medium.com/starbugs/%E9%9D%A2%E8%A9%A6-%E8%80%83%E9%A9%97%E4%BD%A0%E5%B0%8D-promise-%E7%9A%84%E7%86%9F%E5%BA%A6%E4%B9%8B%E9%80%B2%E9%9A%8E%E6%87%89%E7%94%A8%E9%A1%8C-6eda0dd0d

Node 社群

我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。

c11cd841649c24ea64da07f4b499a395.png

   “分享、点赞、在看” 支持一波👍

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值