Lập trình không đồng bộ JavaScript và gọi lại

JavaScript là đồng bộ theo mặc định và là một luồng. Điều này có nghĩa là mã không thể tạo luồng mới và chạy song song. Tìm hiểu mã không đồng bộ có nghĩa là gì và nó trông như thế nào

Tính không đồng bộ trong ngôn ngữ lập trình

Máy tính không đồng bộ theo thiết kế.

Không đồng bộ có nghĩa là mọi thứ có thể xảy ra độc lập với luồng chương trình chính.

Trong các máy tính tiêu dùng hiện nay, mọi chương trình đều chạy trong một khoảng thời gian cụ thể, và sau đó nó sẽ dừng việc thực thi để cho một chương trình khác tiếp tục thực thi. Thứ này chạy trong một chu kỳ nhanh đến mức không thể nhận thấy, và chúng tôi nghĩ rằng máy tính của chúng tôi chạy nhiều chương trình đồng thời, nhưng đây là một ảo ảnh (ngoại trừ trên các máy đa xử lý).

Các chương trình sử dụng nội bộlàm gián đoạn, một tín hiệu được phát ra đến bộ xử lý để thu hút sự chú ý của hệ thống.

Tôi sẽ không đi sâu vào nội dung của vấn đề này, nhưng chỉ cần lưu ý rằng các chương trình không đồng bộ là điều bình thường và tạm dừng thực thi chúng cho đến khi chúng cần chú ý và máy tính có thể thực thi những thứ khác trong thời gian chờ đợi. Khi một chương trình đang đợi phản hồi từ mạng, nó không thể tạm dừng bộ xử lý cho đến khi yêu cầu kết thúc.

Thông thường, các ngôn ngữ lập trình là đồng bộ và một số cung cấp cách quản lý tính không đồng bộ, trong ngôn ngữ hoặc thông qua các thư viện. C, Java, C #, PHP, Go, Ruby, Swift, Python, tất cả chúng đều đồng bộ theo mặc định. Một số trong số chúng xử lý không đồng bộ bằng cách sử dụng các luồng, tạo ra một quy trình mới.

JavaScript

JavaScript làđồng bộtheo mặc định và là một luồng. Điều này có nghĩa là mã không thể tạo luồng mới và chạy song song.

Các dòng mã được thực thi theo chuỗi, nối tiếp nhau, ví dụ:

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

Nhưng JavaScript được sinh ra bên trong trình duyệt, công việc chính của nó lúc đầu là phản hồi các hành động của người dùng, nhưonClick,onMouseOver,onChange,onSubmitvà như thế. Làm thế nào nó có thể làm điều này với một mô hình lập trình đồng bộ?

Câu trả lời là trong môi trường của nó. Cáctrình duyệtcung cấp một cách để làm điều đó bằng cách cung cấp một tập hợp các API có thể xử lý loại chức năng này.

Gần đây hơn, Node.js đã giới thiệu một môi trường I / O không chặn để mở rộng khái niệm này sang quyền truy cập tệp, cuộc gọi mạng, v.v.

Gọi lại

Bạn không thể biết khi nào người dùng sẽ nhấp vào một nút, vì vậy những gì bạn làm là, bạnxác định một trình xử lý sự kiện cho sự kiện nhấp chuột. Trình xử lý sự kiện này chấp nhận một hàm, hàm này sẽ được gọi khi sự kiện được kích hoạt:

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

Đây là cái gọi làgọi lại.

Gọi lại là một hàm đơn giản được truyền dưới dạng giá trị cho một hàm khác và sẽ chỉ được thực thi khi sự kiện xảy ra. Chúng ta có thể làm điều này vì JavaScript có các hàm hạng nhất, có thể được gán cho các biến và được truyền cho các hàm khác (được gọi làchức năng bậc cao hơn)

Thông thường, gói tất cả mã khách hàng của bạn trong mộtloadngười nghe sự kiện trênwindowđối tượng, chỉ chạy chức năng gọi lại khi trang đã sẵn sàng:

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

Gọi lại được sử dụng ở mọi nơi, không chỉ trong các sự kiện DOM.

Một ví dụ phổ biến là sử dụng bộ hẹn giờ:

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

Yêu cầu XHR cũng chấp nhận một lệnh gọi lại, trong ví dụ này bằng cách gán một hàm cho một thuộc tính sẽ được gọi khi một sự kiện cụ thể xảy ra (trong trường hợp này, trạng thái của yêu cầu thay đổi):

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()

Xử lý lỗi trong các cuộc gọi lại

Làm thế nào để bạn xử lý lỗi với các cuộc gọi lại? Một chiến lược rất phổ biến là sử dụng những gì Node.js đã thông qua: tham số đầu tiên trong bất kỳ hàm gọi lại nào là đối tượng lỗi:gọi lại lỗi đầu tiên

Nếu không có lỗi, đối tượng lànull. Nếu có lỗi, nó chứa một số mô tả về lỗi và thông tin khác.

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

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

Sự cố với các cuộc gọi lại

Gọi lại rất tốt cho các trường hợp đơn giản!

Tuy nhiên, mọi lệnh gọi lại đều thêm một mức lồng ghép và khi bạn có nhiều lệnh gọi lại, mã bắt đầu phức tạp rất nhanh:

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

Đây chỉ là một mã 4 cấp đơn giản, nhưng tôi đã thấy nhiều cấp độ lồng vào nhau hơn và nó không thú vị.

Làm thế nào để chúng tôi giải quyết điều này?

Các lựa chọn thay thế cho các cuộc gọi lại

Bắt đầu với ES6, JavaScript đã giới thiệu một số tính năng giúp chúng ta với mã không đồng bộ không liên quan đến việc sử dụng lệnh gọi lại:

Tải xuống miễn phí của tôiSổ tay dành cho Người mới bắt đầu JavaScript


Các hướng dẫn js khác: