1. Synchronous vs. Asynchronous
Với GCD, bạn có thể thực hiện 1 task đồng bộ hoặc không đồng bộ.
Một hàm đồng bộ (Synchronous) được trả về sau khi task completes. Ta có thể dùng hàm đồng bộ bằng cách gọi DispatchQueue.sync(execute:).
Một hàm bất đồng bộ (Asynchronous) trả về ngay lập tức, sắp xếp thứ tự bắt đầu nhưng không chờ nhiệm vụ khác kết thúc. Do đó 1 hàm bất đồng bộ không block current thread từ quá trình execution của nó đến function tiếp theo. Ta có thể dùng hàm bất đồng bộ bằng cách gọi DispatchQueue.async(execute:).
2. Ví dụ cụ thể:
a. Handle background tasks:
Các bạn download project và giới thiệu sơ qua ở đây.
Mình có một ví dụ cụ thể sau. Đó là khi click vào cái hình càng lớn, nó sẽ bị lag một khoảng thời gian trước khi qua màn hình kia.
Lưu ý khoảng thời gian để chế độ xem chi tiết ảnh hiển thị. Độ trễ thấy rõ hơn khi xem hình ảnh lớn trên các thiết bị chậm hơn.
Ta thường dễ bị issue lagging này khi đặt các đoạn code take nhiều thời gian ở trong viewDidLoad của 1 viewController. Dẫn đến việc ta phải chờ khá lâu cho đến khi view xuất hiện. Tốt nhất là giảm tải cái việc này xuống background nếu nó hoàn toàn không cần thiết tại thời điểm load.
Theo debug thì 2 đoạn code gây ra việc tốn nhiều thời gian chính là đoạn này:
let overlayImage = faceOverlayImageFrom(image) fadeInNewImage(overlayImage)
Thay bằng đoạn code sau:
// 1
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let self = self else {
return
}
let overlayImage = self.faceOverlayImageFrom(self.image)
// 2
DispatchQueue.main.async { [weak self] in
// 3
self?.fadeInNewImage(overlayImage)
}
}
Hiện tại thì:
Nó sẽ lấy cái hình + ghép mắt zô -> xong rồi mới hiển thị qua bên kia
Muốn fix như là hiển thị cái hình qua bên kia -> xong mới ghép cái mắt zô.
Các bước như sau:
1. Bạn move đống việc lấy hình + ghép mắt này quăng vào bên queue background global, và run đống ni trong 1 closure 1 cách bất đồng bộ. Việc này cho phép viewDidLoad() finish nhanh hơn trên main thread và làm cho việc load hình ảnh cảm giác mượt hơn. Cùng lúc đó, cái việc face detection processing được bắt đầu và sẽ kết thúc lúc nào đó.
2. Lúc này, quá trình detect face đã xong và ta đã generate ra 1 hình ảnh mới. Vì là sau khi ta đã có được ảnh mới, ta đập cái ảnh mới này vào ảnh cũ ở UIImageView, rồi bạn add 1 cái closure mới vào hàng main. Lưu ý là modifies UI phải trên main thread.
Cuối cùng, ta update UI bằng fadeInNewImage (_ :) nó sẽ thực hiện chuyển đổi mờ dần của hình ảnh đôi mắt googly mới.
Rõ ràng có sự thay đổi trước và sau. Cho dù bạn có up 1 hình thiệt nặng, nó vẫn sẽ không hang-out. Nói chung, bạn muốn sử dụng async khi bạn cần thực hiện tác vụ dựa trên mạng hoặc CPU chuyên sâu trong nền và không chặn luồng hiện tại.
3. Khi nào dùng cái nào:
- Main Queue: Để update UI sau khi ta done 1 task ở 1 concurrent queue thì đây là cách hay dùng. Để làm thì cách làm như sau: ta code 1 closure bên trong 1 closure khác. Nhắm đến Main queue và gọi async để đảm bảo rằng cái task mới này sẽ được execute lúc nào đó sau khi current method xong. Tức là thằng ViewDidLoad xong nó mới tiến hành.
- Global Queue: Update 1 nhiệm vụ mà ko phải UI thì ta nên dùng cái này.
- Custom Serial Queue: Đây là 1 sự lựa chọn ok khi ta muốn perform 1 background work và theo dõi nó. Nó giúp hạn chế xung đột resource máy và các điều kiện khác khi mà chỉ có duy nhất 1 task ở 1 thời điểm nhất định. Lưu ý nếu bạn cần data từ 1 method, bạn phải khai báo 1 closure khác để lấy nó ra hoặc xem thử có nên dùng sync không.