如何组合这两种异步方法的结果?

How can I combine the result of these 2 async methods?

我调用了 2 个方法。我需要生成一个包含两者结果的模型并调用另一个方法。

我想避免将 1 个方法放在另一个方法中,因为这可能会扩展到 3 或 4 个额外的调用。

基本上,一旦我得到 setUserFollowedStateloadFollowersForTopic 的结果,我想将这两个值发送到另一个函数。

来自 JS 领域,我会使用 async/await,但这在 Swift 中不存在。

  func setUserFollowedState() {
    following.load(for: userID, then: { [weak self, topic] result in
      guard self != nil else { return }
      let isFollowed = (try? result.get().contains(topic)) ?? false
      // do something with isFollowed?

    })
  }

  func loadFollowersForTopic() {
    followers.load(topic, then: { [weak self, topic] result in
      guard self != nil else { return }
      let count = (try? result.get().first(where: { [=10=].tag == topic })?.followers) ?? 0
      // do something with count?
    })
  }

您可以将两个异步调用结果存储为可选属性。当您的回调发生时,设置这些属性,然后检查是否已设置这两个属性。如果已设置,则您知道两个异步调用都已返回。

private var isFollowed: Bool?
private var count: Int?

func setUserFollowedState() {
    following.load(for: userID, then: { [weak self, topic] result in
        guard let self = self else { return }
        let isFollowed = (try? result.get().contains(topic)) ?? false

        self.isFollowed = isFollowed
        performPostAsyncCallFunctionality()
    })
}

func loadFollowersForTopic() {
    followers.load(topic, then: { [weak self, topic] result in
        guard let self = self else { return }
        let count = (try? result.get().first(where: { [=10=].tag == topic })?.followers) ?? 0

        self.count = count
        performPostAsyncCallFunctionality()
    })
}

private func performPostAsyncCallFunctionality() {
    // Check that both values have been set.
    guard let isFollowed = isFollowed, let count = count else { return }

    // Both calls have returned, do what you need to do.
}

这种方法的好处是您可以使用该模式轻松添加更多异步调用。但是,如果您需要一次进行那么多异步网络调用,我建议您考虑重写服务器端逻辑,这样您只需要一次网络调用即可实现此功能。

另一种方法(我认为它更简洁)是使用 DispatchGroup 来组合上述方法的结果。

您将修改原始方法以采用完成处理程序,然后在您实际需要数据的地方合并两个结果。 请参阅下面的示例。

func setUserFollowedState(completion: @escaping ((Bool) -> Void)) {
  following.load(for: userID, then: { [weak self, topic] result in
    guard self != nil else { return }
    let isFollowed = (try? result.get().contains(topic)) ?? false
    // Call completion with isFollowed flag
    completion(isFollowed)
  })
}

func loadFollowersForTopic(completion: @escaping ((Int) -> Void)) {
  followers.load(topic, then: { [weak self, topic] result in
    guard self != nil else { return }
    let count = (try? result.get().first(where: { [=10=].tag == topic })?.followers) ?? 0
    // Call completion with follower count
    completion(count)
  })
}

func loadFollowedAndCount() {
    let group = DispatchGroup()

    var isFollowed: Bool?
    // Enter group before triggering data fetch
    group.enter()
    setUserFollowedState { followed in
        // Store the fetched followed flag
        isFollowed = followed
        // Leave group only after storing the data
        group.leave()
    }

    var followCount: Int?
    // Enter group before triggering data fetch
    group.enter()
    loadFollowersForTopic { count in
        // Store the fetched follow count
        followCount = count
        // Leave group only after storing the data
        group.leave()
    }

    // Wait for both methods to finish - enter/leave state comes back to 0
    group.notify(queue: .main) {
        // This is just a matter of preference - using optionals so we can avoid using default values
        if let isFollowed = isFollowed, let followCount = followCount {
            // Combined results of both methods
            print("Is followed: \(isFollowed) by: \(followCount).")
        }
    }
}

编辑:始终确保 group.enter() 后跟 group.leave()