Sử dụng async/await trong Swift.

Cơ chế mới xử lý mới async/await đã được giới thiệu ở Swift 5.5 tại WWDC 2021 tho thấy sự tập trung và ưu tiên trong việc phát triển cơ chế concurency của Apple. Mặc dù sẽ mất nhiều thời gian nữa đê async/await có thể trở nên phổ biến và dùng nhiều trong công

  • Cơ chế mới xử lý mới async/await đã được giới thiệu ở Swift 5.5 tại WWDC 2021 tho thấy sự tập trung và ưu tiên trong việc phát triển cơ chế concurency của Apple. Mặc dù sẽ mất nhiều thời gian nữa đê async/await có thể trở nên phổ biến và dùng nhiều trong công việc phát triển app nhưng rõ ràng ta đã thấy sự thu hẹp đáng kể về khoảng cách cũng như thân thiện hơn với các developer trong việc xử lý concurency.

1/ Thế nào là async/await ?

  • Chúng ta sẽ mất khá nhiều thời gian nếu đi chi tiết trong việc định nghĩa async/await nhưng để ngắn gọn thì chúng ta có thể hiểu là nó cho phép chúng ta đánh dấu function bất đồng bộ trong code với từ khóa async và yêu cầ chúng ta từ khóa await để gọi các function đó. Hệ thống sẽ tự động tổ chức cho chúng ta công việc như đợi chờ thực hiện công việc gì đó khi các function này hoàn thành mà không cần nhét vào trong block để xử lý như trước.

  • Lấy ví dụ như ở structDocumentLoaderfunc được đánh dấu async, func này sử dụng APIURLSession để tiến hành xử lý công việc call network bất đồng bộ với từ khóa await để downloaddata từ URL được cho:

structDocumentLoader{var urlSession =URLSession.shared
    var decoder =JSONDecoder()funcloadDocument(withID id:Document.ID)asyncthrows->Document{let url =urlForForLoadingDocument(withID: id)let(data,_)=tryawait urlSession.data(from: url)returntry decoder.decode(Document.self, from: data)}...}
  • Với từ khóa async đặt trong các function thì khi gọi các funtion bất đồng bộ khác thì ta cần thêm từ khóa await để các tiến trình xử lý trong function đó được xếp thứ tự hoàn thành theo thứ các await được triển khai mà không cần sợ có các block được xử lý ở thời điểm mà chúng ta không xác định rõ.

2/ Gọi async function từ các synchorous context:

  • Một câu hỏi được đặt ra là làm sao để chúng ta có thể gọi function được đánh dấu async từ các context mà bản thân nó hoạt không hoạt động bất đồng bộ. Nếu chúng ta muốn sử dụng DocumentLoader bên trên với UIKit thì chúng ta cần đưa functionloadDocument vào trong một Task như sau để tiến hành xử lý bất đồng bộ:
classDocumentViewController:UIViewController{privatelet documentID:Document.IDprivatelet loader:DocumentLoader...privatefuncloadDocument(){Task{do{let document =tryawait loader.loadDocument(withID: documentID)display(document)}catch{display(error)}}}privatefuncdisplay(_ document:Document){...}privatefuncdisplay(_ error:Error){...}}
  • Chúng ta đã sử dụng cơ chế mặc định do / try / catch trong cơ chế error handling để tiến hành xử lý các công việc bất đồng bộ nhưng chúng ta không cần quan tâm đến nhưng thứ như weak self để tránh việc retain cycle. Thậm chí chúng ta không cần phải tự xử lý tiến hành cập nhật UI ở trên main queue.

3/ Cài tiến API bất đồng bộ sẵn có với async/await:

  • Chúng ta sẽ cùng tìm hiểu một cách thức khác để cải tiến cơ chế xử lý hoạt động bất đồng bộ bằng cách thêm vào async/await pattern. Chúng ta sẽ cùng triển khai một CommentLoader để load các comment với việc sử dụng complietion handler:
structCommentLoader{...funcloadCommentsForDocument(
        withID id:Document.ID,
        then handler:@escaping(Result<[Comment],Error>)->Void){...}}
  • Nhìn như có vẻ chúng ta sẽ cần thêm async/await nhưng trong trường hợp này thì không nhé. Chúng ta sẽ tiến hành xử lý trong extention của CommentLoader với functionloadCommentsForDocument sử dụng function mới withCheckedThrowingContinuation để có thể wrap và gọi function sẵn có với từ khóa async như sau:
extensionCommentLoader{funcloadCommentsForDocument(
        withID id:Document.ID)asyncthrows->[Comment]{tryawait withCheckedThrowingContinuation { continuation inloadCommentsForDocument(withID: id){ result inswitch result {case.success(let comments):
                    continuation.resume(returning: comments)case.failure(let error):
                    continuation.resume(throwing: error)}}}}}
  • Với cách triển khai trên giờ chúng ta có thể dễ dàng gọi methodloadCommentsForDocument sử dụng từ khóa await như khi gọi các API bất đồng bộ khác. Và sau đây là cách chúng ta updateDocumentLoader một cách tự động với các comment được fetch về mỗi khi document được load:
structDocumentLoader{var commentLoader:CommentLoadervar urlSession =URLSession.shared
    var decoder =JSONDecoder()funcloadDocument(withID id:Document.ID)asyncthrows->Document{let url =urlForForLoadingDocument(withID: id)let(data,_)=tryawait urlSession.data(from: url)var document =try decoder.decode(Document.self, from: data)
        document.comments =tryawait commentLoader.loadCommentsForDocument(
    withID: id
)return document
    }}

4/ Sử dụng single output của Publisher trong Combine:

  • Chúng ta cùng xem xét khả năng xử lý async/await mạnh mẽ của Combine. Với cơ chế gửi đi value trong stream thì tất cả công việc chúng ta là đợi chờ một result được trả về từ luồng xử lý của Combine. Tiếp tục sử dụng technique chúng ta đã triển khai trước đó với việv mở rộng protocol Publisher với methodsingleResult sẽ trả về chúng ta giá trị đầu tiên và duy nhất được emit bởi Publisher. Chúng ta vẫn sẽ sử dụng closure để xử lý cơ chế retain giải phóng instanceAnyCancellable khi mà các công việc xử lý dữ liệu đã xong.
extensionPublishers{structMissingOutputError:Error{}}extensionPublisher{funcsingleResult()asyncthrows->Output{var cancellable:AnyCancellable?var didReceiveValue =falsereturntryawait withCheckedThrowingContinuation { continuation in
            cancellable =sink(
                receiveCompletion:{ completion inswitch completion {case.failure(let error):
                        continuation.resume(throwing: error)case.finished:if!didReceiveValue {
                            continuation.resume(
                                throwing:Publishers.MissingOutputError())}}},
                receiveValue:{ value inguard!didReceiveValue else{return}

                    didReceiveValue =true
                    cancellable?.cancel()
                    continuation.resume(returning: value)})}}}
  • Đến đây thì chúng ta sẽ dễ nhận ra sự tiện lợi và hiệu quả của việc xử dụng APICombine để xử lý cơ chế async/await như thế nào so với việc sử dụng closure như cách làm phổ thông:
structCommentLoader{...funcloadCommentsForDocument(
        withID id:Document.ID)->AnyPublisher<[Comment],Error>{...}}...let comments =tryawait loader
    .loadCommentsForDocument(withID: documentID).singleResult()

Nguồn: viblo.asia

Bài viết liên quan

Thay đổi Package Name của Android Studio dể dàng với plugin APR

Nếu bạn đang gặp khó khăn hoặc bế tắc trong việc thay đổi package name trong And

Lỗi không Update Meta_Value Khi thay thế hình ảnh cũ bằng hình ảnh mới trong WordPress

Mã dưới đây hoạt động tốt có 1 lỗi không update được postmeta ” meta_key=

Bài 1 – React Native DevOps các khái niệm và các cài đặt căn bản

Hướng dẫn setup jenkins agent để bắt đầu build mobile bằng jenkins cho devloper an t

Chuyển đổi từ monolith sang microservices qua ví dụ

1. Why microservices? Microservices là kiến trúc hệ thống phần mềm hướng dịch vụ,