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

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ