JavaScript sử dụng đơn luồng lý do tại sao?

Nội dung bài viết

Sau một thời gian sử dụng javascript thì tôi biết có nhiều bạn vẫn đang đi tìm câu hỏi này giống như mà tôi đã đi tim cách đây nhiều năm khi mới học về javascript. Nếu bạn nào đã từng nghe cụm từ "Bất đồng bộ trong javascript" và có search trên Ông anh của mình là Mr. Google thì những bài viết đó hầu như là giải thích sai về cụm từ này. Dạng giống như để SEO vậy, nên viết một cách mù quáng. 


Và chốt lại cho các bạn biết hoạt động không đồng bộ javascript nó khác hoàn toàn với lập trình bất đồng bộ javascript. Vì sao? Tôi sẽ giải thích cho các bạn hiểu về hai khái niệm này.


Serires lịch sử javascript


Phần 1: Vì sao javascript là mô hình đơn luồng

Phần 2: Hàng đợi tác vụ và sự kiên vòng lặp trong javascript


Lập trình bất đồng bộ javascript


Đầu tiên nói về xử lý bất đồng bộ javascript hiện này đều đề cập tới những cụm từ như Promise, Callback, Async/await. Ngoài ra rộng hơn nữa bạn phải nắm những khái niệm như Blocking IO, noBlocking IO, và đồng bộ và bất đồng bộ. Nói nhanh về bất đồng bộ hiểu nôm na là một người có thể làm được nhiều việc một lúc, có thể vừa đánh răng mà vừa xem phim... và ngược lại. 


Nói ở đây cho các anh chị hiểu một chút, vì Lập trình bất đồng bộ javascript thì chúng ta đã thảo luận rất nhiều và từ lâu rồi, chứ không phải đến bây giờ nữa. Cho nên ở đây nói ít và chủ yếu chúng ta làm rõ khái niệm "hoạt động không đồng bộ javascript".


Hoạt động không đồng bộ javascript


Đây chính là mục tiêu của tôi muốn đề cập đến cho các bạn, thật ra nó rất nhiều điều để bàn đến ở đây: 


  • JavaScript chỉ chạy trên một luồng 
  • Tác vụ đồng bộ và tác vụ không đồng bộ trong javascript 
  • Hàng đợi tác vụ và vòng lặp sự kiện 
  • Chế độ hoạt động không đồng bộ 
  • Kiểm soát quá trình hoạt động không đồng bộ javascript


Vì vậy đầu tiên chúng ta sẽ tìm hiểu khái niệm như tiêu đề của nó "JavaScript sử dụng đơn luồng lý do tại sao?"


JavaScript Mô hình luồng đơn


Mô hình một luồng có nghĩa là JavaScript chỉ chạy trên một luồng. Nói cách khác, JavaScript chỉ có thể thực hiện một tác vụ tại một thời điểm và các tác vụ khác phải được xếp hàng sau. Lưu ý rằng JavaScript chỉ chạy trên một luồng, không có nghĩa là công cụ JavaScript chỉ có một luồng. Trên thực tế, JavaScript engine có nhiều luồng, một tập lệnh đơn chỉ có thể chạy trên một luồng (được gọi là luồng chính) và kết hợp với các luồng khác trong background. Lý do tại sao JavaScript sử dụng đơn luồng thay vì đa luồng thì để giải thích về điều này chúng ta đi tìm hiểu lịch sử phát triển của javascript. 


JavaScript đã là một luồng kể từ khi nó ra đời. Lý do là bạn không muốn làm cho trình duyệt quá phức tạp, vì nhiều luồng cần chia sẻ tài nguyên và có thể sửa đổi kết quả chạy của nhau. Điều này quá phức tạp đối với một ngôn ngữ kịch bản web . Nếu JavaScript có hai luồng cùng một lúc, một luồng thêm nội dung vào nút DOM của trang web và luồng kia xóa nút, trình duyệt nên sử dụng luồng nào vào lúc này? Có cần thiết phải có cơ cấu khóa không? Do đó, để tránh sự phức tạp, ngay từ đầu JavaScript đã là một luồng, điều này đã trở thành một tính năng cốt lõi của ngôn ngữ và sẽ không thay đổi trong tương lai.


Ưu và nhược điểm của mô hình đơn luồng


Ưu điểm của mô hình này là nó tương đối đơn giản để thực hiện và môi trường thực thi tương đối đơn giản; nhược điểm là chỉ cần một nhiệm vụ mất nhiều thời gian, các tác vụ tiếp theo phải được xếp hàng đợi, điều này sẽ làm chậm quá trình thực thi toàn bộ chương trình. Các trình duyệt thông thường không phản hồi, thường là do một đoạn mã JavaScript nào đó chạy trong thời gian dài (chẳng hạn như vòng lặp vô hạn) khiến toàn bộ trang bị kẹt chỗ này, chỗ khác không thực hiện được. Bản thân ngôn ngữ JavaScript không chậm, cái chậm là đọc và ghi dữ liệu bên ngoài, chẳng hạn như đợi các yêu cầu Ajax trả về kết quả. Lúc này, nếu máy chủ của bên kia lâu không phản hồi, hoặc mạng không thông suốt sẽ khiến script bị dừng trong thời gian dài. 


Nếu hàng đợi là do một lượng lớn các phép tính và CPU quá bận, điều đó tốt, nhưng phần lớn thời gian CPU không hoạt động vì các hoạt động IO (đầu vào và đầu ra) rất chậm (ví dụ: các hoạt động Ajax đọc dữ liệu từ mạng) và phải đợi Sau khi có kết quả, hãy chuyển sang bước tiếp theo. Các nhà thiết kế của ngôn ngữ JavaScript nhận ra rằng tại thời điểm này, CPU hoàn toàn có thể bỏ qua hoạt động IO, tạm dừng các tác vụ đang chờ và chạy các tác vụ được xếp hạng sau đó. Chờ cho đến khi thao tác IO trả về kết quả, sau đó quay lại và tiếp tục thực hiện tác vụ bị treo. 


Cơ chế này là cơ chế "Vòng lặp sự kiện" được JavaScript sử dụng nội bộ. Mặc dù mô hình một luồng đặt ra một hạn chế lớn đối với JavaScript, nhưng nó cũng mang lại cho nó những lợi thế mà các ngôn ngữ khác không có. Nếu bạn sử dụng nó tốt, các chương trình JavaScript sẽ không bị chặn, đó là lý do tại sao Node có thể sử dụng rất ít tài nguyên để đối phó với việc truy cập có lưu lượng truy cập cao đồng thời trong Node.js 


Để tận dụng sức mạnh tính toán của CPU đa lõi, HTML5 đề xuất tiêu chuẩn Web Worker, cho phép các đoạn mã JavaScript tạo nhiều luồng, nhưng các luồng con hoàn toàn được kiểm soát bởi luồng chính và không được vận hành DOM. Do đó, tiêu chuẩn mới này không thay đổi bản chất của JavaScript đơn luồng. 


Đến đây có thể dừng lại ở đây cho việc giải thích "JavaScript single thread", tôi sẽ còn hứng thú để quay lại lịch sử tìm hiểu tiếp... Đừng bỏ lỡ những bài viết tiếp theo nhoé!


Có thể bạn đã bị missing