博客 / 詳情

返回

《javascript高級程序設計》學習筆記 | 11.1.異步編程

關注前端小謳,閲讀更多原創技術文章

異步編程

  • ES6 新增了正式的Promise引用類型,支持更優雅地定義和組織異步邏輯
  • 接下來的幾個版本,使用asyncawait關鍵字定義異步函數的機制

相關代碼 →

同步與異步

  • 同步行為在內存中順序執行處理器指令

    • 每條指令都在單個線程按出現順序執行
    • 每條指令執行後,都可以推斷出程序的狀態,並立即獲得存儲在系統本地(或寄存器或系統內存)的信息
let x = 3 // 操作系統在棧內存上分配一個存儲浮點數值的空間
x = x + 4 // 針對這個值做一次數學計算,並把計算結果寫回之前分配的內存中
  • 異步行為類似於系統中斷

    • 當前進程外部的實體可以觸發代碼執行,通常在定時回調中執行
    • 執行線程不知道何時將信息存儲到系統本地(或寄存器或系統內存),取決於回調合適從消息隊列出列並執行
let x2 = 3 // 操作系統在棧內存上分配一個存儲浮點數值的空間
setTimeout(() => {
  x = x + 4 // 執行線程不知道x值何時會改變,取決於回調何時從消息隊列出列並執行
}, 1000)

以往的異步編程方式

  • 早期的 JS 只支持回調函數表明異步操作,串聯多個異步操作需深度嵌套回調函數(回調地獄
function double(value) {
  setTimeout(() => {
    setTimeout(() => {
      console.log(value * 2)
    }, 2000) // 2000毫秒後,JS運行時會把回調函數推到消息隊列上等待執行
  }, 1000) // 1000毫秒後,JS運行時會把回調函數推到消息隊列上等待執行
}
double(3) // 6(約3000毫秒後),double()函數在setTimeout成功調度異步操作後,立即退出

異步返回值

  • setTimeout操作返回有用的值,可給異步操作提供一個回調,把這個值傳給需要它的地方
function double2(value, callback) {
  setTimeout(() => {
    callback(value * 2) // 1000毫秒後,把回調函數推到消息隊列上
  }, 1000)
}
double2(3, (x) => console.log(`I was given: ${x}`)) // 'I was given: 6'(約1000毫秒後)

失敗處理

  • 在回調模型中做異步操作的失敗處理(成功回調 & 失敗回調)
  • 該方法已不可取,因為必須在初始化異步操作時定義回調,異步函數返回值只在短時間內存在,必須預備好將該返回值作為參數的回調才能接收到它
function double3(value, success, failure) {
  setTimeout(() => {
    // 必須在初始化異步操作時定義回調
    try {
      if (typeof value !== 'number') {
        throw 'Must provide number as first argument'
      }
      success(value * 2)
    } catch (error) {
      failure(error)
    }
  }, 1000)
}
const successCallback = (x) => console.log(`Success: ${x}`)
const failureCallback = (e) => console.log(`Failure: ${e}`)
double3(3, successCallback, failureCallback) // 'Success: 6'(約1000毫秒後)
double3('3', successCallback, failureCallback) // 'Failure: Must provide number as first argument'(約1000毫秒後)

嵌套異步回調

  • 異步返回值依賴另一個異步返回值,則需要嵌套回調回調地獄,不具有擴展性,代碼難以維護)
function double4(value, success, failure) {
  setTimeout(() => {
    try {
      if (typeof value !== 'number') {
        throw 'Must provide number as first argument'
      }
      success(value * 2)
    } catch (error) {
      failure(error)
    }
  }, 1000)
}
const successCallback2 = (x) => {
  double4(x, (y) => console.log(`Success: ${y}`)) // 異步返回值依賴另一個異步返回值,嵌套回調產生“回調地獄”
}
const failureCallback2 = (e) => console.log(`Failure: ${e}`)
double4(3, successCallback2, failureCallback2) // 'Success: 12'(約2000毫秒後)

總結 & 問點

  • 在執行線程和內存存儲方面,同步行為和異步行為有哪些區別?
  • 寫一段代碼,將 setTimeout 的返回值作為回調傳給函數,並執行這個函數獲取結果
  • 寫一段代碼,在回調模型中做異步操作的成功/失敗處理,並説説為什麼該方法已經不可取
  • 回調地獄是如何形成的?其有什麼缺點?
user avatar chongdianqishi 頭像 _raymond 頭像 buxia97 頭像 weirdo_5f6c401c6cc86 頭像 gaoming13 頭像
5 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.