JavaScript異步編程和回調

JavaScript默認情況下是同步的,並且是單線程的。這意味著代碼無法創建新線程並不能並行運行。找出異步代碼的含義以及它的外觀

編程語言中的異步性

計算機在設計上是異步的。

異步意味著事情可以獨立於主程序流發生。

在當前的消費計算機中,每個程序都運行特定的時間段,然後停止執行,以讓另一個程序繼續執行。這件事的運行週期如此之快,以至於無法察覺,我們認為我們的計算機可以同時運行許多程序,但這是一種幻想(在多處理器計算機上除外)。

程序內部使用打斷,該信號被發送到處理器以引起系統的注意。

我不會深入探討這一點,只是要記住,程序異步是正常的,並且在需要注意之前停止執行,並且計算機可以同時執行其他操作。當程序正在等待來自網絡的響應時,它無法在請求完成之前停止處理器。

通常,編程語言是同步的,並且某些編程語言提供了一種通過語言或通過庫來管理異步性的方法。 C,Java,C#,PHP,Go,Ruby,Swift,Python,它們在默認情況下都是同步的。其中一些通過使用線程來處理異步操作,從而產生一個新進程。

JavaScript

JavaScript是同步默認情況下為單線程。這意味著代碼無法創建新線程並不能並行運行。

代碼行是一個接一個地依次執行的,例如:

const a = 1
const b = 2
const c = a * b
console.log(c)
doSomething()

但是JavaScript誕生於瀏覽器內部,一開始它的主要工作是響應用戶的操作,例如onClickonMouseOveronChangeonSubmit等等。如何使用同步編程模型來做到這一點?

答案在於它的環境。這瀏覽器通過提供一組可以處理這種功能的API來提供一種實現方法。

最近,Node.js引入了非阻塞I / O環境,以將該概念擴展到文件訪問,網絡調用等。

回呼

您不知道用戶何時單擊按鈕,所以您要做的是,為click事件定義事件處理程序。此事件處理程序接受一個函數,該函數將在觸發事件時被調用:

document.getElementById('button').addEventListener('click', () => {
  //item clicked
})

這就是所謂的打回來

回調是一個簡單的函數,將其作為值傳遞給另一個函數,並且僅在事件發生時才執行。我們這樣做是因為JavaScript具有一流的功能,這些功能可以分配給變量並傳遞給其他功能(稱為高階函數

通常將所有客戶端代碼包裝在一個load上的事件監聽器window對象,僅在頁面準備就緒時才運行回調函數:

window.addEventListener('load', () => {
  //window loaded
  //do what you want
})

回調無處不在,不僅在DOM事件中使用。

一個常見的示例是使用計時器:

setTimeout(() => {
  // runs after 2 seconds
}, 2000)

XHR請求還接受回調,在此示例中,將一個函數分配給一個屬性,該屬性將在發生特定事件(在這種情況下,請求的狀態發生變化)時被調用:

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
  if (xhr.readyState === 4) {
    xhr.status === 200 ? console.log(xhr.responseText) : console.error('error')
  }
}
xhr.open('GET', 'https://yoursite.com')
xhr.send()

處理回調中的錯誤

您如何處理回調錯誤?一種非常常見的策略是使用Node.js所採用的方法:任何回調函數中的第一個參數是錯誤對象:錯誤優先回調

如果沒有錯誤,則對象為null。如果有錯誤,它將包含對該錯誤的一些描述以及其他信息。

fs.readFile('/file.json', (err, data) => {
  if (err !== null) {
    //handle error
    console.log(err)
    return
  }

//no errors, process data console.log(data) })

回調的問題

回調適用於簡單情況!

但是,每個回調都會添加一個嵌套級別,並且當您有很多回調時,代碼會很快變得非常複雜:

window.addEventListener('load', () => {
  document.getElementById('button').addEventListener('click', () => {
    setTimeout(() => {
      items.forEach(item => {
        //your code here
      })
    }, 2000)
  })
})

這只是一個簡單的4層代碼,但是我看到了更多的嵌套層,這很不好玩。

我們該如何解決呢?

回調的替代方法

從ES6開始,JavaScript引入了一些功能,這些功能可以幫助我們處理不涉及使用回調的異步代碼:

免費下載我的JavaScript初學者手冊


更多js教程: