Ajax

Asynchronous operations

Có lẽ bạn đã làm nhiều về các asynchronous operations rồi, chẳng hạn như các AsyncTask trong Android hay Promise trong Javascript. Tuy nhiên, dù là vậy nhưng đa phần các bạn mới vào nghề chỉ làm theo quán tính, tức là lúc còn được học thì thấy thầy cô giáo sử dụng các cái class, interface hay method đó rồi quen tay mà áp dụng cho tới tận sau này, khi trình độ tay nghề đã lên cao, “máu me” dữ lắm nhưng khi được hỏi là tại sao lại dùng như vậy thì họ khó giải thích được. Bài này sẽ đem lại cái nhìn tổng quan nhất về asynchronous operations.

async-all-the-things

1. Process và Thread

Dù là ứng dụng mang tính nội bộ (local) cho máy tính hay di động, cũng như ứng dụng web, từ ngôn ngữ Java, Javascript, Qt C++ tới Qml, C# của Visual Studio hay bất kì cái nào khác, thì bạn cũng đi từ hướng xây dựng giao diện, gán các sự kiện khi người dùng tương tác với các thành phần giao diện đó, chẳng hạn các buttons, các ô nhập liệu, các pop-up menu… Điều này quá hiển nhiên rồi. Tuy nhiên, cái có thể bạn ít để ý là mọi thao tác từ render giao diện tới phản hồi các sự kiện trên đều diễn ra trên một luồng, tức là UI Thread. Như vậy, bạn có thể tạm coi là UI Thread là thread trực tiếp liên quan đến giao diện hiện hữu mà bạn đang thao tác, cái đối tượng mà bạn đang thấy được.

Hiện đang có một số tranh cãi về việc có nên đồng nhất Main thread và UI thread hay không. Một số ý kiến cho rằng Main thread chính là UI thread, và việc render UI chỉ là một công việc của Main thread mà thôi, và như vậy các services cũng chạy trên Main thread. Một số ý kiến khác cho rằng UI thread không phải là Main thread. UI ở đây chỉ bao gồm những thành phần hữu hình, tức là cửa sổ (windows), các nút bấm (buttons), các ô nhập liệu (inputs), các ô hiện ảnh (imageview), v.v… còn các thành phần vô hình thì không phải là UI nên chúng sẽ không chạy trên UI thread. Một số các quan điểm khác nữa thì cho là UI không nhất thiết chỉ là về giao diện hữu hình mà còn bao gồm luôn các thành phần vô hình, vì hành động sử dụng ứng dụng chẳng qua là sự tương tác với ứng dụng, mà đương nhiên tương tác là phải tương tác với giao diện của ứng dụng. Cho dù là ứng dụng không có một cửa sổ cụ thể, nhưng khi ta mở ứng dụng lên thì ta đã tương tác với nó rồi và vô hình trung là ta đã tương tác với giao diện của nó. Tuy nhiên, theo cách hiểu nào, thì Background threads là những threads được sinh ra từ các UI threads. Về phần Background threads thì không có quá nhiều bàn cãi, các quan điểm tương đối thống nhất với nhau và tôi nghĩ cái tên nó đã nói lên tất cả rồi, nên sẽ không giải thích nhiều lời ở phần này.

Và bao giờ cũng vậy, khi lập trình thì một trong những ưu tiên hàng đầu – dù có đồng nhất Main thread và UI thread hay không – là tránh thực hiện quá nhiều công việc trên UI thread. Và đó là sự ra đời của các asynchronous operations: Các hoạt động nền mang tính bất đồng bộ.

Bạn có thể tham khảo định nghĩa Process và Thread tại Wikipedia hoặc Android Developer page, và từ đó rút ra cách hiểu của riêng bạn. Nhưng dù hiểu dư lào, thì cái câu tô đậm bên trên là điều bạn phải luôn ghi nhớ, bởi vì nó luôn đúng.

2. Asynchronous operation là gì và tầm quan trọng của nó:

Không chỉ gói gọn trong lập trình, đôi khi trong cuộc sống hàng ngày, ta cũng thực hiện một số async ops đó thôi. Chẳng hạn hiện tại, bạn đang là một nhân viên mới toanh, chỉ vừa công tác tại công ty được vài tháng. Đùng một cái, hôm nay, vợ chồng giám đốc, vợ chồng phó giám đốc, vợ chồng trưởng phòng, vợ chồng kế toán trưởng, và các đồng nghiệp khác cùng “gấu” của họ đột nhiên tới nhà bạn chơi để cho biết. Khách tới nhà, thì một trong số các việc chào mời tối thiểu cần làm là mời nước. Giả sử bạn đã có sẵn ly, nước đá và nước uống sẵn rồi, nên bạn sẽ tự “chế biến” đồ uống. Tuy nhiên, bạn có để ý là bạn không bao giờ được phép đập nước đá, cho vào ly và chế nước vào trước mặt khách không? Tất nhiên, bạn sẽ nói “Các anh chị chờ một lát, em đi làm nước, lát em đem lên.” và bạn sẽ bê mớ ly, đá và chai nước sang một nơi khác để thực hiện. Và việc di chuyển sang nơi khác để thực hiện tác vụ chính là một async op.

Việc code ứng dụng cũng hoàn toàn tương tự. Các tác vụ nặng sẽ nên được cắt sang một Background thread mới để thực hiện, để tránh gây đơ UI thread sinh ra cái Background thread đó. Đương nhiên rồi, bạn làm nước trước mặt các ông bà khách tai to mặt lớn kia thì họ đương nhiên sẽ cảm thấy khó chịu vì đợi quá lâu, bạn cũng khó lòng tập trung làm nước được, và như vậy các sếp sẽ dễ trở nên ghét bạn. Còn về mặt lập trình ứng dụng, việc gây đơ các UI thread sẽ làm người dùng cảm thấy khó chịu vì máy sẽ bị treo, mặc dù tình trạng này chỉ mang tính nội bộ ứng dụng mà không gây ảnh hưởng nhiều tới hệ thống. Và hệ quả tất yếu, không quá bất ngờ, là người dùng sẽ ngay và luôn gỡ cài đặt ứng dụng của bạn và thề sẽ không bao giờ cài nó vào lại thêm một lần nào nữa, và đương nhiên họ sẽ vô hình trung có ác cảm với các ứng dụng khác của bạn, mặc dù có cái họ chưa từng dùng qua và cũng “không thèm có ý định dùng làm gì bởi cái tay viết ứng dụng này có tay nghề chắc chắn là dở ẹc”.

Như vậy, asynchronous operation là một hoạt động được diễn ra mà không cần/buộc phải diển ra đồng bộ với các hoạt động khác, cho dù là chúng đều được bắt đầu tại cùng một thời điểm. Mục đích của nó, như tôi đã nói, là tách các hoạt động không liên quan tới nhau ra, không phụ thuộc vào nhau ra, để “đường em em đi, đường anh anh đi”, tránh ảnh hưởng tới UI thread, cái nào xong trước thì xong, cái nào chưa xong mà mất tới 2 phút, 3 giờ, 4 ngày, 5 tuần, 6 tháng hay 7 năm sau mới chạy xong thì cứ bình tĩnh mà chạy từ từ, các threads khác không có hối thúc.

Trong bài tiếp theo, tôi sẽ giới thiệu về callback. Hẹn gặp lại các bạn ở bài đó. Happy coding.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.