Swift Task.init()的操作时间为1.14秒,而完成处理程序的操作时间为0.73秒

5fjcxozz  于 2022-12-02  发布在  Swift
关注(0)|答案(1)|浏览(155)

我试图将Firebase操作从旧的完成处理程序重构到新的Task.init(),但现在似乎花费了更长的时间。我做错了什么吗?等待调用没有同时完成吗(这就是我同时调用这两个操作并使用完成处理程序方法计算完成了多少个操作的原因)?对于可能导致执行时间变慢的原因,您有什么建议吗?
先谢谢你了。
1.完井装卸机接近(0.73s)
以前我有过这种方法:

extension Firebase {
    static func getAll<T: Decodable>(_ subCollection: SubCollection,
                                     completion: @escaping (_ result: [T]?, _ error: Error?) -> Void) {
        db.collection("userData").document(currentUser!.uid).collection("\(subCollection)").getDocuments(completion: { (querySnapshot, error) in
            var documents: [T] = []
            if let error { print(error.localizedDescription) }
            if let querySnapshot {
                for document in querySnapshot.documents {
                    if let decodedDocument = try? document.data(as: T.self) { documents.append(decodedDocument) }
                    else { print("Failed to decode a retrieved document of type \(T.self) at getAll") }
                }
            }
            completion(documents, error)
            AppNotification.post(.firebseOperationCompleted)
        })
    }
}

我会这样使用它:

class Loading: UIViewController {

    var error: Error?
    var operationsCompleted = 0
    let start = CFAbsoluteTimeGetCurrent()

    private func fetchData() {
        operationsCompleted = 0

        Firebase.getAll(.goals, completion: { (results: [Goal]?, error) in
            if let results { UltraDataStorage.goals = results }
            if let error { self.error = error }
            self.operationsCompleted += 1
        })

        Firebase.getAll(.ideas, completion: { (results: [Idea]?, error) in
            if let results { UltraDataStorage.ideas = results }
            if let error { self.error = error }
            self.operationsCompleted += 1
        })

    @objc func advanceWhenOperationsComplete(){
        print("Took \(CFAbsoluteTimeGetCurrent() - self.start) seconds")
        if operationsCompleted == 2 {
            // Proceed if error is nil
        }
    }

    override func viewDidLoad() {
        fetchData()
        AppNotification.observe(handler: self, name: .firebseOperationCompleted, function: #selector(advanceWhenOperationsComplete))
    }
}
  1. Task.init()接近(1.14秒)
    现在,我更新了getAll函数:
extension Firebase {
    static func getAll<T: Decodable>(_ subCollection: SubCollection) async -> Result<[T], Error> {
        do {
            let documents = try await db.collection("userData").document(currentUser!.uid).collection("\(subCollection)").getDocuments()
            var decodedDocuments: [T] = []
            for document in documents.documents {
                if let decodedDocument = try? document.data(as: T.self) { decodedDocuments.append(decodedDocument) }
                else { print("Failed to decode a retrieved document of type \(T.self) at getAll") }
            }
            return.success(decodedDocuments)
        }
        catch { return.failure(error) }
    }
}

我现在这样称呼它

class Loading: UIViewController {

    var error: Error?        
    let start = CFAbsoluteTimeGetCurrent()

    private func fetchData() {
        Task.init(operation: {
            let goalsResult: Result<[Goal], Error> = await Firebase.getAll(.goals)
            switch goalsResult {
            case .success(let goals): UltraDataStorage.goals = goals
            case .failure(let error): self.error = error
            }

            let ideasResult: Result<[Idea], Error> = await Firebase.getAll(.ideas)
            switch ideasResult {
            case .success(let ideas): UltraDataStorage.ideas = ideas
            case .failure(let error): self.error = error
            }

            DispatchQueue.main.async {
                self.advanceWhenOperationsComplete()
            }
        })

    }

    func advanceWhenOperationsComplete(){
        print("Took \(CFAbsoluteTimeGetCurrent() - self.start) seconds")
        // Proceed when the async operations are completed
    }

    override func viewDidLoad() {
        fetchData()
    }
}
wrrgggsh

wrrgggsh1#

性能差异可能是由于完成处理程序模式并发地运行请求,而async-await副本是按顺序执行的。后者在启动下一个异步请求之前等待第一个异步请求的结果。
要使它们并发运行,可以使用async let模式(请参见SE-0317)或使用任务组:

extension Firebase {
    static func getAll<T: Decodable>(_ subCollection: SubCollection) async throws -> [T] {
        try await db
            .collection("userData")
            .document(currentUser!.uid)
            .collection("\(subCollection)")
            .getDocuments()
            .documents
            .map { try $0.data(as: T.self) }
    }
}

// you could use `async let`

private func fetchData1() async throws {
    async let goals: [Goal] = Firebase.getAll(.goals)
    async let ideas: [Idea] = Firebase.getAll(.ideas)

    UltraDataStorage.goals = try await goals
    UltraDataStorage.ideas = try await ideas

    advanceWhenOperationsComplete()
}

// or task group

private func fetchData2() async throws {
    try await withThrowingTaskGroup(of: Void.self) { group in
        group.addTask { UltraDataStorage.goals = try await Firebase.getAll(.goals) }
        group.addTask { UltraDataStorage.ideas = try await Firebase.getAll(.ideas) }

        try await group.waitForAll()
    }

    advanceWhenOperationsComplete()
}

(这些可能不是100%正确的,因为我没有实现所有这些类型和方法,因此无法编译它,但希望它说明了这个想法。值得注意的是,我对UltraDataStorage的线程安全性感到紧张,特别是在任务组示例中。但这超出了本问题的范围。)
总之,async let是一种并发运行任务的直观方式,在处理固定的、有限数量的异步任务时最有用。任务组在处理可变数量的任务时很有用。这里不是这种情况,但为了完整起见,我将其包括在内。
请注意,我已经删除了Result<[T], Error>类型,而代之以throw错误。

相关问题