gpt4 book ai didi

swift - 带 DispatchGroup 的并行 URLSession 请求在 1 个请求上调用完成处理程序两次

转载 作者:行者123 更新时间:2023-11-30 10:31:41 24 4
gpt4 key购买 nike

使用 DispatchGroup 我尝试对我的客户端运行 2 个网络请求,并在两个请求完成后返回结果。

我遇到一个问题,有时 DispatchGroup 请求之一的完成处理程序会被调用两次,而另一个请求根本不会被调用。

一个例子是 -


func fetchProfileWithRelatedArticle(onSuccess: @escaping (User, [RelatedArticle]) -> Void, onError: @escaping (Error) -> Void) {
let dispatchGroup = DispatchGroup()

var user: User?
var articles: [RelatedArticle] = []

var errors: [Error] = []

dispatchGroup.enter()
fetchProfileForUser(onSuccess: {
user = $0
print("fetchProfile:",$0)
print("123")
dispatchGroup.leave()
}, onError: { error in
errors.append(error)
dispatchGroup.leave()
})

dispatchGroup.enter()
getArticlesForUser(onSuccess: {
articles = $0
print("getArticlesForUser:",$0)
print("456")
dispatchGroup.leave()
}, onError: { error in
errors.append(error)
dispatchGroup.leave()
})

dispatchGroup.notify(queue: .main) {
guard let user = user, errors.isEmpty else { return }

onSuccess(user, articles)
}
}

在这里,我获取用户个人资料,还获取他们撰写的文章列表。这些通过完成处理程序返回并在其他地方呈现。

大多数情况下,这是有效的,但有时会出现这些请求之一会调用自己的完成处理程序两次,而另一个请求则不会的情况。

我怀疑这可能是因为我的访问 token 过期,因为如果我短暂离开应用程序,就会发生这种情况。我的访问 token 的有效期为 2 分钟。

如果请求收到 401 响应,我的网络客户端中有以下方法来请求新 token ,然后再次调用该调用。我相信这可能不会如我所愿。


if response.statusIs401() {
self?.refreshHandler { success in
guard success else { completion(.failure(TokenError.refused)); return }
self?.request(resource, completion)
}
return
}

我怀疑在更新对我的调度组返回的请求执行某些操作后再次调用该方法。

是否可以以这种方式链接请求?


struct NoContent: Codable { }
typealias RefreshHandler = (@escaping (Bool) -> Void) -> ()
typealias TokenGetter = () -> [String: String]

protocol ClientType: class {
associatedtype Route: RouterType
func request<T: Codable>(_ resource: Route, _ completion: @escaping (Result<T>)-> Void)
}

class Client<Route: RouterType>: ClientType {

enum APIError: Error {
case unknown, badResponse, jsonDecoder, other
}

enum TokenError: String, Error {
case expired = "Access Token Expired"
case refused = "Refresh Token Failed"
}

private(set) var session: SessionType
private(set) var tokenGetter: TokenGetter
private(set) var refreshHandler: RefreshHandler

private lazy var decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601withFractionalSeconds
return decoder
}()

init(session: SessionType, tokenGetter: @escaping TokenGetter, refreshHandler: @escaping RefreshHandler) {
self.session = session
self.tokenGetter = tokenGetter
self.refreshHandler = refreshHandler
}

func request<T: Codable>(_ resource: Route, _ completion: @escaping (Result<T>)-> Void) {

let request = URLRequest(
resource: resource,
headers: tokenGetter()
)

URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
guard error == nil else { completion(.failure(APIError.unknown)); return }
guard let response = response as? HTTPURLResponse else { completion(.failure(APIError.badResponse)); return }

if response.statusIs401() {
self?.refreshHandler { success in
guard success else { completion(.failure(TokenError.refused)); return }
self?.request(resource, completion)
}
return
}

if response.statusIsSuccess() {
guard let self = self, let data = self.deserializeNoContentResponse(data: data) else { completion(.failure(APIError.badResponse)); return }
do {
let value = try self.decoder.decode(T.self, from: data)
DispatchQueue.main.async {
completion(.success(value))
}
} catch let error {
print(error)
}
return
}

completion(.failure(APIError.other))
}.resume()
}

// some calls return a 200/201 with no data
private func deserializeNoContentResponse(data: Data?) -> Data? {

if data?.count == 0 {
return "{ }".data(using: .utf8)
}

return data
}
}

最佳答案

听起来您需要在网络客户端中执行以下操作:

func makeTheRequest(_ completion: CompletionHandler) {
URLSession.shared.dataTask(with: someURL) { data, response, error in
guard let httpResponse = response as? HTTPURLResponse else {
return
}

if httpResponse.statusCode == 401 {
self.refreshToken { success in
if success { self.makeTheRequest(completion) }
}
}

// handle response
completion(whateverDataYouNeedToPass)
}
}

这将进行调用,检查响应代码,刷新需要的 token ,如果成功,它会调用应该使用首先传递到的完成处理程序再次进行响应的方法,而不是先调用它。因此,直到第二次调用 API 后才会调用完成处理程序。

当然,将其应用于您自己的代码,应该不会太难做到

关于swift - 带 DispatchGroup 的并行 URLSession 请求在 1 个请求上调用完成处理程序两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59070190/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com