Trang chủ Development

Chia sẻ kiến thức về đồng bộ và không đồng bộ trong JavaScript

Chia sẻ kiến thức về đồng bộ và không đồng bộ trong JavaScript

Đối với những người bắt đầu tiếp cận lĩnh vực lập trình, cụ thể là khía cạnh ngôn ngữ lập trình, có lẽ trong ngôn ngữ JavaScript thì đồng bộ và không đồng bộ là những khái niệm tương đối "khó nhằn".

Có thể cắt nghĩa đơn giản về đồng bộ là "hai hay nhiều việc diễn ra vào cùng một thời điểm", tất nhiên, nếu chúng diễn ra vào các thời điểm khác nhau, tức là không đồng bộ.

Bạn sẽ nhanh chóng thấy rằng thực tế những khái niệm này phức tạp và khó hiểu hơn so với những gì mình đang nghĩ. "Cái gì được đồng bộ" và "cái gì không đồng bộ" là những điều mà chúng ta cần phải xác định rõ ràng, chính xác tuyệt đối.

JavaScript - một ngôn ngữ lập trình của xử lí không đồng bộ

Trong các loại ngôn ngữ chính của lập trình web hiện nay, JavaScript được coi là ngôn ngữ có tính phổ biến cao và cũng là ngôn ngữ dễ sử dụng nhất. JavaScript giúp website của chúng ta thêm phần sinh động thay vì chỉ chuyên nội dung như HTML hay chuyên về phong cách như CSS.  Khi ai đó đưa ra nhận xét rằng JavaScript là ngôn ngữ lập trình xử lý không đồng bộ, tức là họ đang muốn nói đến cơ chế xử lý của JavaScript - cơ chế xử lý theo dạng gửi thông điệp một cách lần lượt. 

Một đặc điểm riêng biệt của JavaScript là thay vì gọi hàm một cách trực tiếp, nó sẽ được gọi thông qua các thông điệp. Hệ thống xử lý của JavaScript bao gồm các thành phần: Hàng đợi thông điệp (Message queue), Vòng lặp sự kiện (Event-Loop) và Cấu trúc dữ liệu dạng ngăn xếp (Call Stack). 

Chia sẻ kiến thức về đồng bộ và không đồng bộ trong JavaScript - Ảnh 1.

Message queue đảm nhận nhiệm vụ tiếp nhận và lưu giữ các thông điệp, mỗi thông điệp trong hàng đợi tương ứng với một hàm được gọi. 

Event-Loop thực hiện công việc điều phối, lần lượt lấy thông điệp đang nằm trong hàng đợi đưa đi xử lý. Event-Loop phải chờ đến khi thông điệp trước đó được xử lý xong rồi mới tiếp tục trở lại lấy thông điệp mới từ hàng đợi mang đi xử lý, vòng lặp diễn ra liên tục và lặp lại như thế đến khi hết thông điệp chờ ở Message queue. 

Cấu trúc dữ liệu dạng ngăn xếp hay Call Stack thực hiện xử lý thông điệp, cụ thể có chức năng giữ lời gọi hàm, quản lý thực thi hàm và gọi lồng hàm. Điều này có nghĩa là, mỗi "thông điệp" - "hàm được gọi" được Event-Loop lấy ra từ Message queue đem đi xử lý sẽ tương ứng với một khung trong Call Stack, nếu hàm được gọi này tiếp tục gọi tới các hàm con của nó, thì những hàm con sẽ lần lượt được thêm vào từng khung phía trên của Call Stack. JavaScript sẽ lần lượt xử lý toàn bộ các ngăn chứa đến khi không còn hàm nào thì coi như toàn bộ thông điệp đã được xử lý xong. Bạn có thể hiểu rõ hơn quy trình hoạt động thông qua hình vẽ sau:

Theo như hình vẽ trên ta thấy, khi thông điệp mới nhập vào Message queue, quy trình diễn ra theo các bước: Thông điệp được giữ lại ở hàng đợi, chờ đến khi Call Stack xử lý hết các dữ liệu trong ngăn xếp của thông điệp trước đó thì Event-loop trở lại lấy thông điệp mới đem tới Call Stack để xử lý, thêm các chức năng tương ứng với thông điệp mới.

Một ví dụ khác sẽ cụ thể hóa việc này:

Khi đoạn code trên được chạy, thông điệp được gửi đến Message queue là yêu cầu xử lý của hàm bar(). Event-loop nhận thấy sự xuất hiện của thông điệp, tới Message queue để lấy thông điệp đưa tới Call stack để xử lý, lúc này, tại Call stack sẽ có 1 frame (khung) tương ứng với thông điệp bar, hàm này tiếp tục gọi hàm con của nó - hàm foo() đến nên sẽ có thêm một frame (khung) nữa trong Call stack - frame (khung) của foo, theo quy tắc, nó sẽ nằm trên đầu. Sau đó, Call stack bắt đầu thực hiện xử lý lần lượt 2 frame (khung) của foo và bar, chức năng sẽ lần lượt được xóa khỏi stack ngay sau khi hoàn thành.

Chia sẻ kiến thức về đồng bộ và không đồng bộ trong JavaScript - Ảnh 2.

Giả sử có một thông điệp khác được gửi đến Message queue, thì thông điệp sẽ được giữ lại ở đó đến khi quy trình xử lý dữ liệu ở Call stack hoàn thành thì thông điệp này mới được Event-loop lấy và đưa đến xử lý tại Call stack.

Bạn có thể thấy, trong JavaScript, chúng ta không gọi hàm một cách trực tiếp mà thực hiện gọi bằng cách gửi thông điệp. Như vậy, cái mác ngôn ngữ chạy không đồng bộ mà mọi người gắn cho JavaScript thực tế là gọi tên cơ chế quản lý và xử lý dữ liệu của ngôn ngữ lập trình này. Khái niệm không đồng bộ nằm ở chỗ, ngay khi được gọi, hàm hoàn toàn không được thực hiện mà phải nằm đợi trong Message queue đến khi hàm trước được xử lý xong. 

Thế còn những hàm không đồng bộ thì sao?

Qua những giải thích cơ bản phía trên, có vẻ bạn đã nắm bắt được những thông tin cần thiết. Nhưng không, điều thú vị là "không phải mọi thứ trong ngôn ngữ này đều bất đồng bộ". Nếu đã tìm hiểu, chắc hẳn bạn sẽ thắc mắc liệu những hàm như setTimeout() hoặc AJAX thì cơ chế xử lý có khác gì so với quy trình vừa được đưa ra. Hãy nghiên cứu những thông tin về setTimeout() và AJAX ở phần này để hiểu rõ hơn:

setTimeout() là hàm được sử dụng để giới hạn khoảng thời gian xử lý dữ liệu, hay nói theo cách khác, nếu bạn muốn thông điệp mình gửi đi được xử lý sau một khoảng thời gian nhất định thì bạn sử dụng hàm này. Cú pháp của setTimeout() như sau:

1  setTimeout(function, delay-time, arguments …) ;

Tham số function: hàm xử lý.

Delay-time: khoảng thời gian trì hoãn, đơn vị tính: millisecond.

Arguments: Các đối số mà hàm của bạn gọi đến (các tham số truyền vào)

Chúng ta cùng xem đoạn code được chạy với yêu cầu: in ra dòng chữ "hey" sau 3 giây:

1 setTimeout( function() {console.log("hey"), 3000 ) ;

Xử lý in ra "hey" sẽ không được thực hiện ngay vì có delay-time là 3000 millisecond hay 3 giây, lúc này, một đồng hồ bấm giờ với thời gian 3 giây được kích hoạt, trong khi đó, Call stack vẫn tiếp tục xử lý "thông điệp" tới trước đó. Khi hết thời gian đếm ngược 3 giây thì xử lý in ra "hey" đang nằm trong Message queue sẽ được Event-loop đưa đi xử lý. Tuy nhiên, nếu thông điệp trước đó vẫn đang được Call stack xử lý thì thông điệp in ra dòng chữ "hey" vẫn phải chờ.

AJAX (Asynchronous JavaScript and XML) là khái niệm chỉ kỹ thuật sử dụng các API XMLHttpRequest (XHR) để tạo các request cho server và xử lý các respond trả về. 

Thông thường, nếu không sử dụng XMLHttpRequest, khi tạo request gửi đến server, trang web đang hiển thị sẽ được làm mới và load lại toàn bộ giao diện để hiển thị nội dung mới. Bằng cách sử dụng AJAX tức các request gửi đi và các response trả về được xử lý bởi API XHR thì trang web vẫn có thể hiển thị nội dung mới mà không cần load lại, giao diện của người dùng hoàn toàn không chịu bất  cứ  ảnh hưởng gì.

Như vậy, mục đích cơ bản là tạo ra các request mà không cần thiết phải làm mới, load lại trang web. Vậy, "bất đồng bộ" ở đây  nằm ở chỗ nào?. Việc sử dụng mã XHR hoàn toàn không đồng nghĩa với việc nó là AJAX, bởi trên thực tế XHR API có thể làm việc ở cả hai phương diện: đồng bộ và không đồng bộ.

XHR được thiết kế mặc định để làm việc không đồng bộ, khi một chức năng sử dụng XHR để tạo ra request, xử lý tiếp tục được chạy thay vì phải đợi phản hồi từ server. Nếu chức năng được cấu hình chạy đồng bộ, điều này đồng nghĩa với việc cần thiết phải nhận được phản hồi từ server thì xử lý phía sau mới tiếp tục được thực hiện.

Tiếp tục theo dõi 2 ví dụ phía dưới để hiểu rõ hơn những thông tin trên đây:

Ví dụ 1: 

Trong ví dụ 1, bất kỳ truy cập trực tiếp nào đến dữ liệu response sau send() đều không có nghĩa, vì bản thân send() hoàn toàn không đợi đến khi request  được hoàn thành.  Hãy nhớ kỹ, theo như giải thích ở trên,  XMLHTTPRequest được thiết kế mặc định chạy không đồng bộ.

Ví dụ 2:

Trong ví dụ này, file hello.txt chỉ chứa text đơn giản, thuộc tính response của XHR không hợp lệ nên text sẽ không được in ra. Sau thời gian 1/1000 giây, XHR tự động thực hiện kiểm tra response, đồng thời kích hoạt hoạt sự kiện bổ sung cho các trạng thái mà request đi qua. Khi response được load, XHR kích hoạt sự kiện onload để cung cấp đầu ra hợp lệ. Trong trường hợp muốn response được chạy đồng bộ, ta sẽ truyền false vào để làm đối số cuối cùng cho open, cụ thể ra sao, hãy nhìn vào đoạn code dưới đây:

Đây là làm việc đồng bộ, vì theo mặc định, đối số cuối cùng của open là true,  tức là đối số false đang nhắc nhở API XHR là nó phải chạy đồng bộ.

Theo BizFly Cloud tổng hợp

>> Có thể bạn quan tâm:5 tính năng vô cùng thú vị của Java 9

BizFly Cloud là hệ sinh thái điện toán đám mây được vận hành bởi VCCorp - Công ty dẫn đầu trong lĩnh vực truyền thông và internet tại Việt Nam. Với đội ngũ kỹ thuật viên trình độ cao và kinh nghiệm lâu năm làm việc trên các công nghệ khác nhau như cloud, mobile, web..., chúng tôi có đủ khả năng để hỗ trợ đưa ra những lời khuyên hữu ích và công nghệ toàn diện giúp doanh nghiệp chuyển đổi số thành công. Dành cho độc giả quan tâm tới các dịch vụ đám mây do BizFly Cloud cung cấp có thể truy cập tại đây.