如何防止多次调用异步函数但为它们中的每一个调用完成?

How to prevent from calling async function many times but call completion for each of them?

这是我目前使用的代码:

typealias ResponseHandler = (SomeResponse?, Error?) -> Void

class LoginService {
    private var authorizeTokenCompletions = [ResponseHandler]()
    func authorizeToken(withRefreshToken refreshToken: String, completion: @escaping ResponseHandler) {
        if authorizeTokenCompletions.isEmpty {
            authorizeTokenCompletions.append(completion)
            post { [weak self] response, error in
                self?.authorizeTokenCompletions.forEach { [=10=](response, error) }
                self?.authorizeTokenCompletions.removeAll()
            }
        } else {
            authorizeTokenCompletions.append(completion)
        }
    }

    private func post(completion: @escaping ResponseHandler) {
        // async
        completion(nil, nil)
    }
}

以上代码的思路是什么?

  1. authorizeToken 函数可以根据需要调用任意多次(例如 20 次)
  2. 一次只能推送一个异步请求(post)。
  3. 调用的 authorizeToken 函数的所有完成都应使用与第一个完成的参数相同的参数进行调用。

用法:

let service = LoginService()

service.authorizeToken(withRefreshToken: "") { a, b in print(a)}
service.authorizeToken(withRefreshToken: "") { a, b in print(a)}
service.authorizeToken(withRefreshToken: "") { a, b in print(a)}
service.authorizeToken(withRefreshToken: "") { a, b in print(a)}
service.authorizeToken(withRefreshToken: "") { a, b in print(a)}

上面的所有完成都应该打印第一个被调用的结果。

可以用 RxSwift 做到这一点吗?

PS 一旦有可能帮助我的人,我将奖励 100;)

正在回答

is it possible to do this with RxSwift

这是不可能的,因为每次我们触发函数时它都会被分派,我们无法从其他线程访问回调。

您正在创建竞争条件,解决方法是在单例中填充一次数据,而不是使用该单例多次调用该函数。

其他一些方法也可能有效单例只是一个例子。

Race condition: A race condition is what happens when the expected completion order of a sequence of operations becomes unpredictable, causing our program logic to end up in an undefined state

Is it possible to do this with RxSwift?

是的,这是可能的。 RxSwift and Handling Invalid Tokens.

最简单的解决方案:

func authorizeToken(withRefreshToken refreshToken: String) -> Observable<SomeResponse> {
    Observable.create { observer in
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            print("async operation")
            observer.onNext(SomeResponse())
        }
        return Disposables.create()
    }
}

let response = authorizeToken(withRefreshToken: "")
    .share(replay: 1)

response.subscribe(onNext: { print([=10=]) })
response.subscribe(onNext: { print([=10=]) })
response.subscribe(onNext: { print([=10=]) })
response.subscribe(onNext: { print([=10=]) })
response.subscribe(onNext: { print([=10=]) })

仅当所有请求(subscribes)都在第一个请求完成之前发出时,以上内容才有效。就像你的代码一样。

如果您想要存储响应以便在完成后使用,那么您可以使用 replay 而不是 share

let response = authorizeToken(withRefreshToken: "")
    .replayAll()

let disposable = response.connect() // this calls the async function. The result will be stored until `disposable.dispose()` is called.

response.subscribe(onNext: { print([=11=]) })

DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    response.subscribe(onNext: { print([=11=]) }) // this won't perform the async operation again even if the operation completed some time ago.
}