Tìm hiểu về Async/Await của Swift 5.5

iOS Jun 30, 2021

Tại sự kiện WWDC21 (hội nghị thường niên dành cho lập trình viên của Apple) vừa được tổ chức online. Apple giới thiệu tới cộng đồng các nâng cấp và cải tiến mang đến nhiều tính năng mới cho hệ sinh thái sản phẩm của mình. Trong đó iOS 15, iPadOS 15, watchOS 8 và macOS 12 và Swift 5.5. Trong bài viết này chúng ta sẽ tìm hiểu về các vấn đề trong lập trình bất đồng bộ và xem cách xử lý vấn đề đó như thế nào với từ khóa Async/Await trong Swift 5.5.

Lập trình viên backend thường phải xử lý lượng logic khá lớn cho nên việc viết code chạy bất đồng bộ gần như rất nhiều để giúp hệ thống nhanh phản hồi trở lại với người dùng, đem lại trải nghiệm tối ưu (UX). Đáng kể đến ở đây là NodeJS - là môi trường giúp Javascript chạy phía backend. Bên mobile ta phải chạy bất đồng bộ các tác vụ nặng như đọc/ghi database, file hay request API để tránh block UI.

Để xử lý bất đồng bộ thì điều thiết yếu ở đây là một callback giúp trả về kết quả hay thông báo là đã chạy xong đoạn code bất đồng bộ. Với iOS thì là closure hay higher order function, Kotlin là lambdas. Trong bài viết này chúng ta cứ gọi chung là callback nhé. Một vấn đề trong lập trình bất đồng bộ là các callback lồng nhau. Ví dụ chúng ta chạy bất đồng bộ lấy thông tin AppVersion rồi khi có thông tin version rồi thì ta lấy Config, và khi ta có dữ liệu về config thì ta tiếp tục lấy thông tin profile người dùng. Khi đó trong iOS ta có đoạn code callback lồng nhau như thế này.

Hình 1: ba callback lồng nhau (iOS - Swift).

Hiển nhiên đây là đoạn code khá đơn giản và chỉ với ba callback được lồng nhau. Trên thực tế là mọi người có thể phải xử lý khoảng chục dòng code trong từng callback. Đôi khi còn phải thực hiện nhiều callback hơn - bình thường có thể là 5 tới 6 callback lồng nhau. Ngoài ra là việc khó quản lý lỗi, cú pháp trông phức tạp đối với bạn chưa nhiều kình nghiệm hay quên gọi callback để cập nhật UI… Và trong giới lập trình có một thuật ngữ riêng để chỉ hiện tượng này - đó là Callback Hell. Mọi người có thể hiểu đơn giản là một địa ngục hay nơi rất khủng khiếp của các callback.

Hình 2: rất khó đọc hiểu được đoạn code callback như thế này.

Để tránh Callback Hell, trong Javascript 2015 ta có thể dùng Promise - ta dùng từ khóa then. Hoặc là từ khóa Async/Await trong Javascript 2017. Với từ khóa này ta có thể viết các đoạn code bất đồng bộ nhưng trông giống như đoạn code tuần tự (không đồng bộ) vậy. Trong iOS thì gần như là chưa có giải pháp cho vấn đề này, chúng ta thường phải tự thực thi hoặc dùng thư viện 3rd Promises. Cộng đồng lập trình bằng ngôn ngữ Swift đã yêu cầu bổ sung từ khóa Async/Await từ lâu. Và nay từ khóa đó đã có trên Swift 5.5. Dù mới chỉ ở giai đoạn beta nhưng cũng rất đáng vọc đúng không nào… Và tất cả mọi thứ liên quan, các tổng hợp, đề xuất, thay đổi và cải tiến của Async/Await trong Swift đều được publish tại đây.

Chúng ta đi vào ví dụ luôn nhé. Giả sử chúng ta có bài toán như sau: Đầu tiên ta request lên server và lấy giá trị nhiệt độ (mảng 100000 giá trị nhiệt độ trong khoảng từ -10>30 độ). Tiếp theo khi server trả các giá trị về ta tính giá trị nhiệt độ trung bình. Cuối cùng ta gửi giá trị trung bình đó lại cho server. Khi gọi để thực thi đoạn code trên với callback thì sẽ trông như Hình 4.

Hình 3: ví dụ lập trình bất đồng bộ - với callback (1).
Hình 4: thực thi hàm bất đồng bộ - với callback (2).

Bây giờ chúng ta sẽ áp dụng Async/Await vào ví dụ trên nhé. Trong Swift cần 2 bước rất đơn giản để áp dụng. Bước 1 là thêm từ khóa async vào các hàm bất đồng bộ và bước 2 là gọi hàm đó bằng từ khóa await. Tất nhiên có thể đoạn code bất đồng bộ bị thất bại. Khi đó ta dùng từ khóa async throws để ném ra ngoại lệ (có thể một enum error) và gọi bằng từ khóa try await. Vậy là chúng ta thu được đoạn code trông rất đơn giản, dễ đọc như code tuần tự trong Hình 5 và Hình 6. Để gọi hàm trong Hình 6 ta có thể bọc vào trong một async { ... }.

Hình 5: áp dụng Async/Await vào ví dụ trên (1).
Hình 6: áp dụng Async/Await vào ví dụ trên (2).

Ta cũng có một số quy tắc sau cho Async/Await:

  • Các hàm đồng bộ (Synchronous) sẽ không gọi trực tiếp được các hàm bất đồng bộ
  • Các hàm bất đồng bộ có thể gọi được các hàm bất đồng bộ khác và các hàm đồng bộ bình thường khác
  • Khi các hàm đồng bộ & bất đồng mà giống nhau về cách gọi hàm. Thì Swift sẽ dựa vào ngữ cảnh để gọi.

Hiện tại Async/Await mới chỉ có trên iOS 15, Swift 5.5 và Xcode 13 beta. Cho nên thời gian tới sẽ được liên tục cải tiến. Mọi người có thể theo dõi tại đây, Docs hoặc tham khảo bài tổng hợp về các tính năng mới trong sự kiện WWDC21.

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.