[面試] 考驗你對 Promise 的熟度之進階應用題

前端小智發表於2021-12-21
作者: HannahLin
來源:medium
有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

這一篇應該早在兩年前就要寫了,因為是當初面試某間知名的 Udxxx 線上課程的面試題,現在只能用我薄弱的記憶回憶一下。要說進階題是因為當初我自認為對 Promise 算了解,但遇到這種應用題還是被 KO。

image.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 需要完成的時間都不同。

每個任務所需時間是隨機的,只是這樣示意比較好懂

請使用以下提供的範本,寫出能完成這 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] 如下圖:

image.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:

image.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 裡跑這樣才能把所有任務都完成。


程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

原文:
https://medium.com/starbugs/%...

交流

有夢想,有乾貨,微信搜尋 【大遷世界】 關注這個在凌晨還在刷碗的刷碗智。

本文 GitHub https://github.com/qq44924588... 已收錄,有一線大廠面試完整考點、資料以及我的系列文章。

相關文章