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

时间:2020-04-30 06:15:19

标签: swift asynchronous callback

我有两种调用方法。我需要生成一个包含两者的结果的模型,然后调用另一个方法。

我想避免将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: { $0.tag == topic })?.followers) ?? 0
      // do something with count?
    })
  }

2 个答案:

答案 0 :(得分:1)

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

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: { $0.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.
}

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

答案 1 :(得分:1)

另一种方法(我认为这样更干净)是使用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: { $0.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()