我试图为检查用户名可用性的请求方法设置超时。当用户键入用户名并按下按钮时,将调用checkUsername
方法。我的代码无效,因为Timeout(5.0){}
中的代码永远不会被执行,而timeout
永远不会获得值false
。我知道这不是最好的方法,但我想尝试一下,想知道这是否可以通过某种方式进行修改,还是需要采用不同的方法?
var timeout: Bool = false
func usernameAvailable(username: String) -> String{
let response: String!
response = Server.checkUsername(username!)
Timeout(5.0){
self.timeout = true
}
while(!timeout){
if(response != nil){
return response
}
}
return "Timeout"
}
Timeout.swift
类看起来像这样并且正在运行
class Timeout: NSObject{
private var timer: NSTimer?
private var callback: (Void -> Void)?
init(_ delaySeconds: Double, _ callback: Void -> Void){
super.init()
self.callback = callback
self.timer = NSTimer.scheduledTimerWithTimeInterval(NSTimeInterval(delaySeconds),
target: self, selector: "invoke", userInfo: nil, repeats: false)
}
func invoke(){
self.callback?()
// Discard callback and timer.
self.callback = nil
self.timer = nil
}
func cancel(){
self.timer?.invalidate()
self.timer = nil
}
}
答案 0 :(得分:1)
我知道你要做什么,除非你真的需要/想要编写自己的网络代码,否则使用现有框架会更有意义。
我建议改为使用NSURLRequest
中的timeoutInterval支持以及NSURLSession
上的完成处理程序来实现您正在寻求的解决方案。
服务器响应的超时可以在类似NSURLSessionDataTask
的完成处理程序中处理。
这是一个帮助您开始从iTunes Store检索数据的工作示例,以说明如何处理超时:
let timeout = 5 as NSTimeInterval
let searchTerm = "philip+glass"
let url = NSURL(string: "https://itunes.apple.com/search?term=\(searchTerm)")
let request: NSURLRequest = NSURLRequest(URL: url!,
cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringCacheData,
timeoutInterval: timeout)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task: NSURLSessionDataTask = session.dataTaskWithRequest(request, completionHandler: {
(data, response, error) in
if response == nil {
print("Timeout")
} else {
print(String(data: data!, encoding: NSUTF8StringEncoding))
}
}
)
task.resume()
如果将超时间隔缩短到很短的时间,则可以强制执行超时。
答案 1 :(得分:0)
Timeout
块中的代码将永远不会运行,因为计时器将在主线程上触发,但是您使用while
循环阻止主线程。
您还有另一个问题,即您正在调用Server.checkUsername(username!)
并返回该结果,这表明这必须是同步调用(这不好)。所以,这也可能会阻止那里的主线程。在Timeout
返回之前,它甚至不会尝试启动checkUsername
逻辑。
对此有一些kludgy修复,但在我看来,这需要一个非常不同的模式。永远不应该编写具有轮询while
循环的代码,该循环轮询某些完成状态。采用completionHandler
闭包的异步模式要好得多。但是,如果没有关于checkUsername
正在做什么的更多信息,那么很难更具体。
但是,理想情况下,如果您的checkUsername
正在构建NSMutableURLRequest
,请为此指定timeoutInterval
,然后NSURLSessionTask
完成块检查NSError
domain
NSURLErrorDomain
,代码NSURLError.TimedOut
。如果先前的请求已经在运行,您也可能想要取消它。
func startRequestForUsername(username: String, timeout: NSTimeInterval, completionHandler: (Bool?, NSError?) -> ()) -> NSURLSessionTask {
let request = NSMutableURLRequest(URL: ...) // configure your request however appropriate for your web service
request.timeoutInterval = timeout // but make sure to specify timeout
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
dispatch_async(dispatch_get_main_queue()) {
guard data != nil && error == nil else {
completionHandler(nil, error)
return
}
let usernameAvailable = ... // parse the boolean success/failure out of the `data` however appropriate
completionHandler(usernameAvailable, nil)
}
}
task.resume()
return task
}
然后你可以这样使用它:
private weak var previousTask: NSURLSessionTask?
func checkUsername(username: String) {
// update the UI to say that we're checking the availability of the user name here, e.g.
usernameStatus.text = "Checking username availability..."
// now, cancel prior request (if any)
previousTask?.cancel()
// start new request
let task = startRequestForUsername(username, timeout: 5) { usernameAvailable, error in
guard usernameAvailable != nil && error == nil else {
if error?.domain == NSURLErrorDomain && error?.code == NSURLError.TimedOut.rawValue {
// everything is cool, the task just timed out
} else if error?.domain == NSURLErrorDomain && error?.code != NSURLError.Cancelled.rawValue {
// again, everything is cool, the task was cancelled
} else {
// some error other happened, so handle that as you see fit
// but the key issue that if it was `.TimedOut` or `.Cancelled`, then don't do anything
}
return
}
if usernameAvailable! {
// update UI to say that the username is available
self.usernameStatus.text = "Username is available"
} else {
// update UI to say that the username is not available
self.usernameStatus.text = "Username is NOT available"
}
}
// save reference to this task
previousTask = task
}
顺便说一下,如果你对请求进行这种优雅的异步处理,你也可以增加超时间隔(例如10或15秒)。我们不会冻结用户界面,因此我们可以做任何我们想做的事情,而不是人为地限制请求所允许的时间。