设置请求方法的超时

时间:2015-10-17 21:41:25

标签: ios swift

我试图为检查用户名可用性的请求方法设置超时。当用户键入用户名并按下按钮时,将调用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
}
}

2 个答案:

答案 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秒)。我们不会冻结用户界面,因此我们可以做任何我们想做的事情,而不是人为地限制请求所允许的时间。