Как я могу объединить результат этих 2 asyn c методов? - PullRequest
1 голос
/ 30 апреля 2020

У меня есть 2 метода, которые я вызываю. Мне нужно создать модель, которая содержит результат того и другого, и вызвать другой метод.

Я хотел бы избежать размещения одного метода внутри другого, так как это может расширить до 3 или 4 дополнительных вызовов.

По существу, когда у меня есть результаты для setUserFollowedState и loadFollowersForTopic, я хочу отправить оба значения в другую функцию.

Исходя из 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 ]

1 голос
/ 30 апреля 2020

Другим подходом (который я считаю немного чище) было бы использование 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().

1 голос
/ 30 апреля 2020

Вы можете сохранить оба asyn c результаты вызова в качестве дополнительных свойств. Когда ваши обратные вызовы произойдут, установите эти свойства, а затем убедитесь, что оба свойства были установлены. Если они установлены, вы знаете, что оба асинхронных вызова c вернулись.

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.
}

Хорошая особенность этого подхода заключается в том, что вы можете легко добавить больше асин c вызовов, используя шаблон. Тем не менее, если вам нужно сделать столько сетевых асинхронных вызовов c одновременно, я бы порекомендовал вам подумать о переписывании серверной логики c, поэтому для этой функции вам нужен только один сетевой вызов.

...