我有这种形式的代码:
func myFunction(<...>, completionHandler: (ResponseType) -> Void) {
<prepare parameters>
mySessionManager.upload(multipartFormData: someClosure,
to: saveUrl, method: .post, headers: headers) { encodingResult in
// encodingCompletion
switch encodingResult {
case .failure(let err):
completionHandler(.error(err))
case .success(let request, _, _):
request.response(queue: self.asyncQueue) { response in
// upload completion
<extract result>
completionHandler(.success(result))
}
}
}
}
测试代码如下:
func testMyFunction() {
<prepare parameters>
var error: Error? = nil
var result: MyResultType? = nil
let sem = DispatchSemaphore(value: 0)
var ran = false
myFunction(<...>) { response in
if ran {
error = "ran twice"
return
}
defer {
ran = true
sem.signal()
}
switch response {
case .error(let err): error = err
case .success(let res): result = res
}
}
sem.wait()
XCTAssertNil(error, "Did not want to see this error: \(error!)")
<test response>
}
我使用信号量来阻塞主线程,直到异步处理请求为止;这适用于我所有其他Alamofire请求 - 但不是这个。测试挂起。
(注意事项:使用active waiting不会改变事情。)
使用调试器,我想出了
encodingCompletion
is never called。现在我最好的猜测是DispatchQueue.main.async
说,“当它有时间时在主线程上执行它” - 它永远不会,因为我的测试代码在那里阻塞(并且将进行进一步的测试,无论如何)。
我将其替换为self.queue.async
和upload.delegate.queue.addOperation
,在同一个函数中找到了另外两个排队操作。然后测试运行但产生意外错误;我的猜测是,encodingCompletion
太早称为 。
这里有几个问题要问;任何答案都可以解决我的问题。
DispatchQueue.main
可以进入其他任务吗?答案 0 :(得分:0)
正如here所解释的那样,这是一个糟糕的“解决方案”,因为它会在嵌套请求时引入死锁的可能性。我出于教学目的离开这里。
更改
self.queue.sync {
...
}
{{1}}
解决(阅读:解决)问题。
我不知道这是一个强大的修复还是其他任何东西;我有filed an issue。
答案 1 :(得分:0)
我们不应该阻止主线程。 XCTest有自己的等待异步计算的解决方案:
let expectation = self.expectation(description: "Operation should finish.")
operation(...) { response in
...
expectation.fulfill()
}
waitForExpectations(timeout: self.timeout)
在处理事件时运行运行循环,直到满足所有期望或达到超时。使用此API时,客户端不应操纵运行循环。
在XCTest之外,我们可以使用与XCTestCase.waitForExpectations()类似的机制:
var done = false
operation(...) { response in
...
done = true
}
repeat {
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1))
} while !done
注意:这假定operation
将其工作发送到同一队列本身执行。如果它使用另一个队列,这将不起作用;但是后来使用DispatchSemaphore
(参见the question)的方法不会导致死锁并且可以使用。
XCTest中的实现做了很多(多个期望,超时,可配置的睡眠间隔等),但这是基本机制。