swift - 异步调用全部成功或者都不成功,如何处理

标签 swift asynchronous closures

我正在尝试创建一个在线移动应用程序,但无法找出处理具有多个异步调用的函数的最佳方法。假设我有一个函数以某种方式更新用户,但在单个函数调用中涉及多个异步调用。例如:

// Function caller
update(myUser) { (updatedUser, error) in
    if let error = error {
       // Present some error UI to the user
    }

    if let updatedUser = updatedUser {
       // Do something with the user
    }
} 


// Function implementation
public func updateUser(user: User, completion: @escaping (User?, Error?) -> () {

    // asynchronous call A
    updateUserTable(user: User) { error in
        if let error = error {
           completion(nil, error)
        } else {
           // create some new user object
           completion(user, nil)
        }
    }

    // asynchronous call B
    uploadMediaForUser(user: User) { error in
        if let error = error {
           completion(nil, error)
        }
    }

    // asynchronous call C
    removeOldReferenceForUser(user: User) { error in
         if let error = error {
           completion(nil, error)
        }
    }

    // Possibly any additional amount of asynchronous calls...
}

在这种情况下,更新用户等一个函数调用涉及多个异步调用,这是一种全有或全无的情况吗?例如,假设 updateUserTable() 调用完成,但用户在 uploadMediaForUser() 运行时断开与互联网的连接,从而引发错误。由于 updateUserTable() 完成得很好,我的函数调用者认为此方法成功,而实际上更新用户所涉及的所有内容并未完成。现在,我遇到了一个可能在数据库中引用不匹配或信息错误的用户,因为该用户的连接在通话过程中中断。

我该如何处理这种全有或全无的情况?如果每个异步调用都完成而没有错误,我就知道更新用户是成功的。如果只有部分异步调用成功,而有些则失败,这是很糟糕的,我需要撤消成功的更改或再次尝试失败的方法。

在这种情况下我该怎么办?另外,我如何使用完成闭包来帮助根据方法的成功或失败来确定所需的操作。他们都成功了吗?好,告诉用户。有些人成功了,有些人失败了?不好,恢复更改或重试(我不知道)??

编辑:

仅仅用错误来调用我的完成似乎还不够。当然,用户会看到某些东西失败了,但这无助于应用程序了解修复部分更改的损坏所需的步骤。

最佳答案

我建议为您的任务和返回的结果添加帮助器枚举,例如(用户?,错误?)的情况有一点模糊,例如两者都为零?或者您设置了用户和错误,是否成功?

关于全部成功或部分失败 - 我建议使用 DispatchGroup 来通知所有任务何时完成(并检查它们最终如何完成)。

此外,从您当前的代码来看,当某些请求失败时,不清楚是哪个用户 - 当您传递 nil 时,因此可能会在失败后回滚它带来困难。

因此,在我看来,类似下面的内容(未测试代码,但认为您应该从中领会其中的想法)可以让您控制您所描述的问题:

public enum UpdateTask {
    case userTable
    case mediaUpload
    // ... any more tasks you need
}

public enum UpdateResult {
    case success
    case error([UpdateTask: Error])
}

// Function implementation
public func updateUser(user: User, completion: @escaping (User, UpdateResult) -> ()) {

    let updateGroup = DispatchGroup()
    var tasksErrors = [UpdateTask: Error]()

    // asynchronous call A
    updateGroup.enter()
    updateUserTable(user: User) { error in
        if let error = error {
            tasksErrors[.userTable] = error
        }
        updateGroup.leave()
    }

    // ... any other similar tasks here

    updateGroup.notify(queue: DispatchQueue.global()) { // Choose the Queue that suits your needs here by yourself
        if tasksErrors.isEmpty {
            completion(user, .success)
        } else {
            completion(user, .error(tasksErrors))
        }
    }
}

关于swift - 异步调用全部成功或者都不成功,如何处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49365770/

相关文章:

Javascript (Modernizr/YepNope.js) 语法和用法

javascript - 为什么调用匿名函数表达式有两种不同的语法?

Java:如何异步发出http请求?

javascript - 如何在 1-arg javascript 函数中使用 2 个参数?

java - 线程 "main"java.lang.NoSuchMethodError : com. google.common.collect.ImmutableList.toImmutableList()Ljava/util/stream/Collector 中出现异常;

swift - Swift 自动关闭延迟是如何工作的?

ios - SWRevealViewController - 防止与后 View 交互

swift - 创建一个观察者来检查 MediaPlayer playbackState 是否暂停

swift - 更改作为 SKSpriteNode 的 SKNode 的纹理

c++ - 无堆栈 C++20 协程有问题吗?