Trong các máy tính tiêu dùng hiện tại, mọi chương trình chạy trong một khoảng thời gian cụ thể và sau đó nó dừng thực thi để chương trình khác tiếp tục thực hiện. Thứ này chạy theo chu kỳ nhanh đến mức không thể nhận ra. 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 cùng một lúc, nhưng đây là một ảo ảnh [ngoại trừ trên các máy đa bộ xử lý]
Các chương trình nội bộ sử dụng các ngắt, một tín hiệu được phát ra cho bộ xử lý để thu hút sự chú ý của hệ thống
Bây giờ chúng ta đừng đi sâu vào nội dung của vấn đề này, nhưng hãy nhớ rằng việc các chương trình không đồng bộ và tạm dừng thực thi cho đến khi chúng cần chú ý là điều bình thường, cho phép máy tính 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 và Python đều được đồng bộ hóa theo mặc định. Một số trong số chúng xử lý các hoạt động 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 được đồng bộ theo mặc định và là luồng đơn. Đ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 nối tiếp nhau, chẳng hạn
JScopy
Nhưng JavaScript được sinh ra bên trong trình duyệt, ban đầu công việc chính của nó là phản hồi các hành động của người dùng, như ________ 48, ________ 49, ________ 50, ________ 00, v.v. Làm sao nó có thể làm được điều này với mô hình lập trình đồng bộ?
Câu trả lời là trong môi trường của nó. Trình duyệt cung cấp một cách để thực hiện điều đó bằng cách cung cấp một bộ API có thể xử lý loại chức năng này
Gần đây hơn, Nút. js đã giới thiệu môi trường non-blocking I/O để 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 nút. Vì vậy, bạn xá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 chức năng, chức năng này sẽ được gọi khi sự kiện được kích hoạt
JScopy
Đây là cái gọi là gọi lại
Gọi lại là một chức năng đơn giản được truyền dưới dạng giá trị cho một chức năng 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à chuyển qua các hàm khác [được gọi là các hàm bậc cao hơn]
Thông thường, bạn sẽ bọc tất cả mã máy khách của mình trong trình xử lý sự kiện
1 trên đối tượngJScopy
2, đối tượng này chỉ chạy chức năng gọi lại khi trang đã sẵn sàngJScopy
JScopy
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à bằng cách sử dụng bộ hẹn giờ
JScopy
Yêu cầu XHR cũng chấp nhận gọi lại, trong ví dụ này bằng cách gán chức năng cho 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]
JScopy
Xử lý lỗi trong 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? . js được 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à
3. Nếu có lỗi, nó chứa một số mô tả về lỗi và thông tin khácJScopy
JScopy
Vấn đề với các cuộc gọi lại
Gọi lại là tuyệt vời cho các trường hợp đơn giản
Tuy nhiên, mỗi cuộc gọi lại sẽ thêm một mức độ lồng nhau và khi bạn có nhiều cuộc gọi lại, mã bắt đầu phức tạp rất nhanh
JScopy
Đây chỉ là một mã 4 cấp độ đơn giản, nhưng tôi đã thấy nhiều cấp độ lồng 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 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 tôi xử lý mã không đồng bộ không liên quan đến việc sử dụng lệnh gọi lại. Lời hứa [ES6] và Async/Await [ES2017]
Các ngôn ngữ lập trình như C, Java, C#, PHP, Go, Ruby, Swift và Python đều được mặc định là đồng bộ, một số trong số chúng xử lý không đồng bộ bằng cách sử dụng các luồng và sinh ra một quy trình mới
Đây là một ví dụ về mã đồng bộ
1JScopy
Các dòng mã được thực thi nối tiếp nhau
Vì JavaScript được sinh ra bên trong trình duyệt nên công việc chính của nó ngay từ đầu là phản hồi các hành động của người dùng, như ________ 48, ________ 49, ________ 120, ________ 121, v.v. Làm sao nó có thể làm được điều này với mô hình lập trình đồng bộ?
Trong một số trường hợp, chẳng hạn như khi bạn muốn tìm nạp một số dữ liệu từ máy chủ [có thể mất một khoảng thời gian không xác định], việc chương trình của bạn bị đóng băng hoàn toàn trong khi đợi dữ liệu đó được tải xuống sẽ cực kỳ kém hiệu quả. . Vì vậy, thay vì làm điều đó, thông thường chỉ chạy tác vụ tìm nạp trong nền.
Điều này có nghĩa là nếu bạn có hai chức năng liên tiếp với chức năng A không đồng bộ thì chức năng B sẽ được thực thi trong khi chức năng A vẫn đang chạy. Trong trường hợp này nếu chức năng B phụ thuộc vào dữ liệu mà chức năng A đang tìm nạp, bạn sẽ gặp sự cố.
gọi lạiKhô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
Vấn đề này được giải quyết với các cuộc 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.
Với hàm gọi lại, bạn có thể đảm bảo rằng hàm B chỉ được gọi sau khi hàm A hoàn thành công việc của nó vì hàm A thực sự là hàm chịu trách nhiệm gọi hàm B.
2JScopy
Các cuộc gọi lại rất tốt cho các trường hợp đơn giản, tuy nhiên, mỗi cuộc gọi lại sẽ thêm một mức độ lồng nhau và khi bạn có nhiều cuộc gọi lại, mã bắt đầu trở nên phức tạp rất nhanh
5JScopy
Đây chỉ là một mã 4 cấp độ đơn giản, nhưng bạn đã thấy nhiều cấp độ lồng nhau hơn và nó không vui chút nào
lời hứaBắt đầu với ES6, JavaScript đã giới thiệu một số tính năng giúp chúng tôi xử lý mã không đồng bộ không liên quan đến việc sử dụng lệnh gọi lại
lời hứa là một cách để giải quyết tình trạng khó xử khi gọi lại và ngăn việc viết quá nhiều lệnh gọi lại trong mã của bạn
Khi một lời hứa đã được gọi, nó sẽ bắt đầu ở trạng thái chờ xử lý. Điều này có nghĩa là chức năng người gọi tiếp tục thực thi, trong khi nó đợi lời hứa thực hiện quá trình xử lý của chính nó và cung cấp cho chức năng người gọi một số phản hồi
Tại thời điểm này, hàm gọi đợi nó trả về lời hứa ở trạng thái đã giải quyết hoặc ở trạng thái bị từ chối, nhưng hàm vẫn tiếp tục thực thi trong khi lời hứa thực hiện
Cú pháp hàm tạo cho một đối tượng lời hứa là
6JScopy
Đối tượng
22 kết quả có các thuộc tính bên trongJScopy
23 — ban đầu là “đang chờ xử lý”, sau đó thay đổi thành “đã thực hiện” hoặc “bị từ chối”,JScopy
24 — một giá trị tùy ý do bạn chọn, ban đầu làJScopy
25JScopy
Khi người thi hành hoàn thành công việc, nó sẽ gọi một trong các hàm mà nó lấy làm đối số
26 — để chỉ ra rằng công việc đã hoàn thành thành côngJScopy
- đặt
23 thànhJScopy
28,JScopy
- đặt
24 thànhJScopy
50JScopy
51 — để chỉ ra rằng đã xảy ra lỗiJScopy
- đặt
23 thànhJScopy
53,JScopy
- đặt
24 thànhJScopy
55JScopy
56 vàJScopy
57 — các chức năng này được xác định trước bởi công cụ JavaScript. Vì vậy, chúng ta không cần phải tạo chúng. Thay vào đó, chúng ta nên viết trình thực thi để gọi chúng khi sẵn sàngJScopy
Đây là một ví dụ về hàm tạo Promise và hàm thực thi đơn giản
3JScopy
Sau một giây “xử lý” người thi hành gọi
58 để đưa ra kết quảJScopy
Đó là một ví dụ về việc hoàn thành công việc thành công, một “lời hứa được thực hiện”.
Và bây giờ hãy xem một ví dụ về việc người thi hành từ chối lời hứa có lỗi.
5JScopy
Người thi hành nên làm một việc gì đó thường mất thời gian và sau đó gọi
59 hoặcJScopy
60 để thay đổi trạng thái của đối tượng Promise tương ứngJScopy
lời hứa có thể được tiêu thụ hoặc sử dụng
8JScopy
Chạy
61 sẽ thực hiện lời hứa củaJScopy
62 và sẽ đợi nó giải quyết, sử dụng hàm gọi lạiJScopy
63 và nếu có lỗi, nó sẽ xử lý nó trong hàm gọi lạiJScopy
64JScopy
Không đồng bộ/Đang chờCác lời hứa đã được đưa ra để giải quyết tình huống khó xử gọi lại nổi tiếng, nhưng chúng lại tự đưa ra sự phức tạp và độ phức tạp của cú pháp
Vì JavaScript không đồng bộ ES2017 thậm chí còn đơn giản hơn với cú pháp async/await
Chức năng không đồng bộ
Theo một cách thoải mái hơn, họ giảm bớt sự phức tạp xung quanh các lời hứa và giới hạn “không phá vỡ chuỗi” của các lời hứa xâu chuỗi. Nó dễ hiểu và dễ sử dụng một cách đáng ngạc nhiên
Từ khóa
65 được đặt trước một hàm và từ khóaJScopy
66 làm cho JavaScript đợi cho đến khi lời hứa đó được giải quyết và trả về kết quả của nóJScopy
5JScopy
Việc thêm từ khóa
65 vào bất kỳ chức năng nào có nghĩa là chức năng đó sẽ trả về một lời hứa và bao bọc những điều không hứa hẹn trong đó.JScopy
66 theo nghĩa đen khiến JavaScript đợi cho đến khi lời hứa hoàn thành và sau đó tiếp tục với kết quả. Điều đó không tốn bất kỳ tài nguyên CPU nào, vì động cơ có thể thực hiện các công việc khác trong khi đó. thực thi các tập lệnh khác, xử lý các sự kiện, v.v.JScopy
Dưới đây là một số bài đăng trên blog yêu thích của tôi về Promises và Async/Await coding, nếu bạn đang muốn đọc thêm