HomeOur Team
Checked Continuation and Unsafe Continuation ~ Part 3

Checked Continuation and Unsafe Continuation ~ Part 3

By phuong.bui
Published in Solutions
November 30, 2022
4 min read

Hello m.n, t đã quay trở lại rồi đây. Ngày hôm nay, t sẽ tiếp tục đi tiếp series về những điều thú vị của Concurrency trong swift 5.5. Và chủ đề mà t muốn nói đến trong blog ngày hôm nay là Checked ContinuationUnsafe Continuation. Blog lần này sẽ nhanh thôi, vì vậy chúng ta cùng bắt đầu ngay thôi nào ~

À, dừng khoảng chừng 2s, dành cho những bạn chưa theo dõi các bài viết trước của series này, thì mọi người có thể đọc nó ở đây: Part 1- Cơ bản về async và await [Part 2- Concurrency và Async/Await] (// update sau khi part2 dc publish)

Được rồi, đi thôi!!

1. Checked Continuation.

Ở các phần trước, chúng ta đã biết cách gọi các đoạn code bất đồng bộ trong một đoạn code đồng bộ như nào, nhưng trường hợp ngược lại thì sao? Làm thế nào mà chúng ta có thể chúng ta có thể gọi những đoạn mã đồng bộ ngay trong các xử lý bất đồng bộ?

Bài toán.

Để cho dễ hiểu, thì chúng ta cùng đặt ra một bài toán: “Ta có một danh sách link ảnh, làm thế nào để ngay sau khi download dc ảnh xuống, chúng ta sẽ hiển thị nó ra giao diện người dùng”

Phương pháp truyền thống.

Trước khi đề cập đến phương pháp giải quyết bài toán trên với Concurrency mới, thì hãy thử nghĩ xem với những gì mà m.n đã biết từ trước đến giờ, mọi người giải quyết bài toán này như nào?

Câu trả lời của t chắc cũng là câu trả lời của mọi người đó là sử dụng DispatchQueue - thứ mà chúng ta dùng nó như cơm bữa. Và tất nhiên\, chúng ta sẽ cần phải dùng cả Completion Handle nữa. T sẽ viết đơn giản các đoạn xử lý như sau:

1, Xử lý donwload ảnh với closure. Function cần thêm @escaping để không bị mất đi khi thực hiện bất đồng bộ

func fetchImageOld(from urlString: String, completion:@escaping ((UIImage?) -> Void)) {
guard let url = URL(string: urlString) else {
return completion(nil)
}
let urlRequest = URLRequest(url: url)
URLSession.shared.dataTask(with: urlRequest) { data, response, error in
guard let data = data else {
return completion(nil)
}
let image = UIImage(data: data)
return completion(image)
}.resume()
}

2, Thực hiện gọi func fetchImageOld ở trên và update ảnh đã down vào list và reload lại tableview, sử dụng DispatchQueue

for url in urlList {
fetchImageOld(from: url) { img in
DispatchQueue.main.async {
self.imageList.append(img)
self.tableview.reloadData()
}
}
}

=> Cũng khá đơn giản nhưng lại phải quan tâm khá nhiều thứ:

  • Phải nhớ trả về completion(nil) với trường hợp lỗi
  • Phải nhớ thêm @escaping
  • Phải sử dụng DispatchQueue.main.async để quay lại main thread vì update view phải được thực hiện ở Main

Sử dụng async/ await và checked Continuation.

Với swift 5.5 chúng ta sẽ có thêm một cách để xử lý bài toán trên với async/await. Và function download ảnh có thể viết lại ngắn gọn như sau:

func fetchImage(from urlString: String) async -> UIImage? {
guard let url = URL(string: urlString) else {
return nil
}
let urlRequest = URLRequest(url: url)
let (data, _) = try await URLSession.shared.data(for: urlRequest)
return UIImage(data: data)
}

Tuy nhiên, có một cái khiến t khá đau đầu ở đây là async funclà một function bất đồng bộ và nó chỉ có thể được gọi đến bởi các đoạn code bất đồng bộ khác. Làm thế nào để chúng ta có thể có thể gọi các đoạn mã đồng bộ ngay trong xử lý bất đồng bộ?. Ở đây là việc update ảnh đã tải xuống vào list và load lại tableview - xử lý này bắt buộc phải xảy ra trên luồng code đồng bộMain thread

Rất may là swift 5.5 đã cung cấp cho chúng ta giải pháp bằng cách sử dụng:

  • withCheckedContinuation(): Khi method này được gọi, nó sẽ tạm dừng task hiện tại, sau đó chúng ta sẽ thêm những xử lý cần thực hiện đồng bộ trong closure mà function cung cấp.
  • Để tiếp tục thực hiện xử lý bất đồng bộ đang bị tạm ngừng thì cần phải gọi resume(:) để tiếp tục. Method này có thể trả về một giá trị mà mình mong muốn - cái mà có thể đã được xử lý ở code đồng bộ. Việc gọi resumerất quan trọng để có thể tiếp tục xử lý bất đồng bộ
  • resume chỉ có thể gọi dc 1 lần trong withCheckedContinuation()

T sẽ sửa đoạn code trên một chút như sau:

func fetchImage(from urlString: String) async {
guard let url = URL(string: urlString) else {
return
}
let urlRequest = URLRequest(url: url)
do {
let (data, _) = try await URLSession.shared.data(for: urlRequest)
let image = UIImage(data: data)
let _ = await withCheckedContinuation { c in
// thêm 1 giá trị vào imagelist và reload tableview
self.imageList.append(image)
self.tableView.reloadData()
// gọi resume để quay về xử lý bất đồng bộ
c.resume(returning: image)
}
} catch {
return
}
}

Sau đó, mình sẽ chỉ cần triển khai 1 cách đơn giản như sau:

Task {
// sử dụng TaskGroup để gọi nhiều request cùng lúc.
let _ = await withTaskGroup(of: Void.self, body: { group in
for url in urlList {
group.addTask {
await self.fetchImage(from: url)
return
}
}
})
}

Đó là toàn bộ xử lý cơ bản mà chúng ta cần để giải quyết bài toán mà chúng ta đặt ra bằng cách sử dụng Checked Continuationasync/await. So với cách truyền thống, có thể thấy nó sự ngắn gọn và rõ ràng hơn. Tuy nhiên, không chỉ có vậy xử lý mới đem lại những điểm khác biệt như:

  • Không cần quan tâm đến @escaping nữa.
  • Kết quả nhận được sẽ chờ sau await sau khi xử lý bất đồng bộ xong và cho ra kết quả -> Không lo vấn đề crash app.

2. Unsafe Continuation.

Như đã đề cập ở trên, resume(returning:) chỉ được gọi 1 lần trong withCheckedContinuation. Lý do là vì Xcode sẽ đảm bảo code sẽ an toàn hơn với runtime, đảm bảo tính bảo toàn của các Thread hay các xử lý đồng thời. Tuy nhiên, nếu muốn bỏ qua việc check an toàn và các cảnh bảo của xcode thì có thể sử dụng withUnsafeContinuation.

Việc sử dụng withUnsafeContinuation tương tự với việc dùng withCheckedContinuation và cơ chế hoạt động của unsafe continuation cũng tương tự như như checked continuation với các rules giống nhau. Chỉ khác nó không kiểm tra xem chúng ta có tuân thủ các rules hay không. Điều đó có nghĩa là nếu xảy ra lỗi, nó sẽ không được phát hiện sớm và không nhận dc mô tả rõ ràng về lỗi trong trường hợp bị crash.

Ví dụ điển hình ở đây, và t thì chẳng biết tại sao lại bị crash app 🥺 Screen Shot 2022-12-08 at 17.35.01.png

Tóm lại, unsafe continuation không thêm bất kỳ xử lý nào khác so với checked continuation , nó chỉ xoá tất cả các kiểm tra tính chính xác mà checked continuation đã có.

3. Kết luận

Qua các ví dụ trên, chắc cũng đủ để m.n hiểu hơn về Checked ContinuationUnsafe Continuation nhưng chắc hẳn không biết nên sử dụng cái nào vì nó cũng tương tự nhau. Theo khuyến khích của đội ngũ team Swift và bản thân m, thì nên sử dụng Checked Continuation , cái gì an toàn thì vẫn hơn chứ nhỉ? 😅

Mọi người có thể đọc để hiểu thêm ở đây:

https://www.donnywals.com/the-difference-between-checked-and-unsafe-continuation-in-swift/ https://developer.apple.com/documentation/swift/withcheckedcontinuation(function:_:) https://developer.apple.com/documentation/swift/unsafecontinuation

Hẹn gặp lại mọi người ở những bài viết tiếp theo. Thân ái và quyết thắng 🇻🇳🇻🇳🇻🇳🇻🇳🇻🇳


Tags

swiftiOSConcurrency

Share

phuong.bui

phuong.bui

Developer

Expertise

Related Posts

Concurrency và Async/Await ~ Part 2
Solutions
Concurrency và Async/Await ~ Part 2
December 04, 2022
8 min
© 2023, All Rights Reserved.
Powered By

Quick Links

HomeOur Team

Social Media