一道 promise 亮燈問題的 思路演進 與 深坑 (JS異步機制) 分析

更新:增加使用 async/await 以及箭頭函數的重構代碼

問題

紅燈三秒亮一次,綠燈一秒亮一次,黃燈2秒亮一次;如何讓三個燈按照這個規律不斷交替重複亮燈?(用Promse實現)三個亮燈函數已經存在:

function green() {
  console.log("green");
}

function yellow() {
  console.log("yellow");
}

function red() {
  console.log("red");
}

#思路解析

一道經典的考察 promise 應用的面試題

我們要解決兩個問題,思路如下

###1 控制每盞燈,使其按某節奏閃爍

  • 命名爲 flash 函數,參數爲亮燈函數與時間參數
  • 應該會用到計時器

###2 控制三種燈閃爍的順序

  • 命名爲 control 函數。
  • 應該會用到promise,鏈式調用。

###3 實現重複不斷的循環

  • 遞歸
  • 爲什麼不能使用 while 等實現無限循環

#代碼演進

實現一次週期內的閃爍

通過上面 1、2 兩點,可以得到如下代碼

function flash(cb, time) {}

function control() {
   Promise.resolve()
    .then()
    .then()
    .then()
}

對於 control 函數

promise.then() 方法:

  • 期望的參數爲函數
  • 返回的結果是一個promise

故而,then 方法的參數函數爲

 function() {
     return flash(red, 3000);
 }

control 函數整體爲

function control(promise) {
  promise
    .then(function() {
      return flash(red, 3000);
    })
    .then(function() {
      return flash(green, 1000);
    })
    .then(function() {
      return flash(yellow, 2000);
    });
}

flash 函數

由上可知,它應該返回一個完成態的 promise。

由於要結合定時器使用,採用promise構造函數的寫法,代碼如下

function flash(cb, time) {
  return new Promise(function executor(resolve, reject) {
    setTimeout(() => {
      cb(), resolve();
    }, time);
  });
}

目前,已經實現了一個週期內的亮燈控制。

實現重複閃爍

要實現不斷重複,考慮遞歸

爲什麼 while 不行

當然,說到無限循環,很容易想到 while 循環等方法。事實上,這種辦法行不通。這涉及到 JavaScript 異步運行機制,參考 單線程 JavaScript 的異步機制與經典 for 循環面試題

簡單地說

  • 一次 事件循環 中,同步代碼先入 執行棧 執行,異步代碼分情況將其任務註冊到 任務隊列 中。

  • 只有執行棧清空,主線程 纔會從任務隊列中讀取任務,使其入棧執行

  • 類似 while(true) 這樣的代碼,會永久佔據主線程,使得執行棧永遠不清空。

  • 所以,任務隊列中等待的亮燈任務也就永無出頭之日。

遞歸

燈光閃爍一個週期,即 control 函數運行一次之後,讓 control 函數遞歸調用自身即可。

還是用 then 方法控制流程,加一個 .then() ,其參數按規定是一個函數,在該匿名函數內部調用函數本身,實現遞歸。

control 函數最終如下

function control() {
  Promise.resolve()
    .then(function() {
      return flash(red, 3000);
    })
    .then(function() {
      return flash(green, 1000);
    })
    .then(function() {
      return flash(yellow, 2000);
    })
    .then(function() {
      control();
    });
}

#完整代碼


function green() {
  console.log("green");
}

function yellow() {
  console.log("yellow");
}

function red() {
  console.log("red");
}

function flash(cb, time) {
  return new Promise(function executor(resolve, reject) {
    setTimeout(() => {
      cb(), resolve();
    }, time);
  });
}

function control() {
  Promise.resolve()
    .then(function() {
      return flash(red, 3000);
    })
    .then(function() {
      return flash(green, 1000);
    })
    .then(function() {
      return flash(yellow, 2000);
    })
    .then(function() {
      control();
    });
}

control();

重構

const red = () => console.log('red')
const green = () => console.log('green')
const yellow = () => console.log('yellow')

const controlLight = color => time => (
  new Promise((resolve, reject) => {
    setTimeout(() => {
      color()
      resolve()
    }, time)
  })
)

const controlOrder = async () => {
  await controlLight(red)(3000)
  await controlLight(green)(1000)
  await controlLight(yellow)(2000)
  await controlOrder()
}

controlOrder()
發佈了49 篇原創文章 · 獲贊 40 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章