2015-10-17 130 views
2

我試圖爲檢查用戶名可用性的請求方法設置超時。當用戶輸入用戶名並按下按鈕時,將調用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

你應該描述'checkUsername'是如何實現的。這段代碼的重構從那裏開始。 – Rob

回答

0

Timeout塊中的代碼將永遠不會運行,因爲定時器將在主線程上觸發,但您使用while循環阻塞主線程。

你在這裏還有一個問題,你打電話給Server.checkUsername(username!)並返回結果,這表明這必須是一個同步調用(這是不好的)。所以,這也可能阻止那裏的主線程。它甚至不會嘗試啓動Timeout邏輯,直到checkUsername返回。

有這方面的修復,但在我看來,這要求一個非常不同的模式。一個人不應該編寫具有輪詢一些完成狀態的空轉循環的代碼。與completionHandler關閉採用異步模式要好得多。但沒有關於什麼checkUsername正在做的更多信息,很難得到更具體的。

但是,理想情況下,如果你的checkUsername正在建設一個NSMutableURLRequest,只需指定timeoutInterval爲,然後與的NSURLErrorDomaindomainNSErrorNSURLSessionTask完成塊校驗和的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秒)。我們並不凍結用戶界面,所以我們可以做我們想做的任何事情,而不是人爲限制請求所允許的時間。

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() 

如果減少超時間隔的東西短,您可以強制超時發生。