PromiseKit入門

原文:Getting Started with PromiseKit
做者:Michael Katz
譯者:kmyhycss

異步編程真的讓人頭疼。無論你怎樣當心,老是easy出現臃腫的託付、混亂的完畢句柄以及長時間的代碼調試!git

幸運的是,現在有一個更好的辦法:promise。Promise 可以讓你以基於事件的方式編寫一連串的動做來實現異步。對於需要以肯定順序運行的動做尤事實上用。在本教程中,你將學習怎樣使用第三方框架 PromiseKit 來讓你的異步代碼和頭腦同一時候保持清晰。web

一般,iOS 開發中都會有不少託付和回調。數據庫


你可能寫過不少類似這樣的代碼:編程

  • Y 負責管理 X。

  • 告訴 Y 去抓取 X。
  • 當 X 可用的時候,Y 通知它的託付對象。

Promise 將這樣的如同亂麻的關係理清成這個樣子:json

當 X 可用時,運行 Y。

是否是優雅多了?Promise 還可以將錯誤處理和成功代碼分離開來,致使代碼在處理各類條件的時候更加清晰。它們可以很是好滴解決複雜的、步驟繁多的工做流,比方web登陸,運行 SDK 登陸認證、處理和顯示圖片等。swift

Promise 是比較常見的,它的實現方式也很是多,但在這篇教程中,咱們將學習一個比較時髦的第三方 Swift 框架,叫作 PromiseKit。api

開始

本文的演示樣例項目是 WeatherOrNot,它是一個簡單的實時天氣應用。promise

它的天氣 API 用的是 OpenWeatherMap。訪問這個 API 的模式和概念可以用到隨意 web 服務上。緩存

這裏下載開始項目。PromiseKit 經過 cocoapods 公佈,但開始項目中已經包括了這個 pod。假設你曾經沒用過 CocoaPods,請參考這篇教程

不然請直接在 CocoaPods 中安裝 PromiseKit。除此以外。本教程不需要不論什麼其餘 CocoaPods 知識。

打開 PromiseKitTutorial.xcworkspace,你會看到項目結構很是easy。僅僅有 5 個 swift 文件:

  • AppDelegate.swift: 本身主動生成的 app delegate 文件。
  • BrokenPromise.swift: 這個文件建立了一個空的 promise,用於臨時構成開始項目的一個部分。
  • WeatherViewController.swift: 處理所有與用戶交互的 view controller。

    這也是 Promise 的主要消費者。

  • LocationHelper.swift: 一個輔助文件,用於實現 CoreLocation。

  • WeatherHelper.swift: 一個輔助文件。用於包裝天氣數據提供者。

OpenWeatherMap API

關於天氣數據,這個 app 用 OpenWeatherMap 做爲天氣數據源。和大部分第三方 API 一樣,要訪問這個服務需要獲取一個 API key。別操心。在本教程中使用的是它的免費套餐,全然夠用了。

咱們先獲取一個 API key。

訪問 http://openweathermap.org/appid,先進行註冊。註冊後就可以在 https://home.openweathermap.org/api_keys 這裏找到你的 API key。

複製這個 key,將它粘貼到 WeatherHelper.swift 頭部的 appID 常量中。

測試

運行 app,假設一切正常。你將看到雅典當前的天氣。

呃……這個 app 現在有一個 bug(都會咱們會解決它),因此 UI 顯示可能有點慢。

理解 Promise

在平常生活中的承諾(promise)你確定知道。

好比,你可以許諾本身在完畢本程以後來一本冷飲。

這個敘述中包括了一個動做(「來一杯飲料」),這個動做會在還有一個動做完畢(「完畢這篇教程」)以後發生。變成中的承諾與此類似,即指望某些事情在將來當某些數據到達以後被運行。

承諾被用於實現異步。和傳統方法。比方經過完畢塊或選擇器進行回調不一樣,承諾可能被簡單地進行鏈式鏈接。從而表達一連串異步動做。承諾和 Operation 有點像。也有一個運行生命週期並能被取消。

一個 PromiseKit 中的承諾會運行一個代碼塊。這個代碼塊應當用一個值來知足(或兌現)。

假設這個值被兌現了,代碼塊就會被運行。假設這個塊返回了一個承諾,則這個承諾也會運行(某個值被兌現),以此類推。假設在這個過程當中發生錯誤,一個可選的 catch 塊將替代這個塊運行。

好比。一個 Promisekit 承諾的口語化描寫敘述是這個樣子:

doThisTutorial().then { haveAColdOne() }.catch { postToForum(error) }

關於 PromiseKit 中的承諾

PromiseKit 是承諾的 Swift 實現。它不是惟一實現。僅僅是流行度最高而已。除了提供塊式構造語法。PromiseKit 還提供了對不少常見 iOS SDK 類的封裝和簡單的錯誤處理機制。

要親身體會 promise 是什麼,請看一眼 BrokenPromise.swift 中的這個函數:

func BrokenPromise<T>(method: String = #function) -> Promise<T> { return Promise<T>() { fulfill, reject in let err = NSError(domain: "PromiseKitTutorial", code: 0, userInfo: [NSLocalizedDescriptionKey: "'\(method)' has not been implemented yet."]) reject(err) } }

方法返回了一個新的泛型化的 promise,它是 PromiseKit 中的核心類。它的構造函數使用一個塊參數,這個塊有兩個參數:

  • fulfill: 一個函數,當這個承諾所需的值被兌現時,調用這個函數。
  • reject: 一個函數。當發生錯誤時,調用這個函數。

對於 BrokePromise 來講,代碼僅僅會返回一個錯誤。這個輔助對象用於在你真正實現這個 app 時提示你仍然有功能需要實現。

建立 promise 對象

訪問遠程server是一種最多見的異步操做。所以咱們從簡單的網絡調用開始。

看一眼 WeatherHelper.swift 中的getWeatherTheOldFashionedWay(latitude:longitude:completion:) 方法。

這種方法用指定的經緯度、完畢塊爲參數。抓取天氣數據。

但是。這個完畢塊無論是成功仍是失敗都會被調用。僅僅會添加完畢塊的複雜性。因爲你需要在代碼中對成功和失敗兩種狀況進行處理。

更過度的是。這個完畢塊是在後臺線程中調用的,所以會致使 (accidentally :cough:) 在後臺更新 UI !:[

這裏用 promise實用嗎?答案是確定的!

在 getWeatherTheOldFashionedWay(latitude:longitude:completion:): 後加入方法:

func getWeather(latitude: Double, longitude: Double) -> Promise<Weather> { return Promise { fulfill, reject in let urlString = "http://api.openweathermap.org/data/2.5/weather?lat=\(latitude)&lon=\(longitude)" + "&appid=\(appID)" let url = URL(string: urlString)! let request = URLRequest(url: url) let session = URLSession.shared let dataTask = session.dataTask(with: request) { data, response, error in if let data = data, let json = (try?

JSONSerialization.jsonObject(with: data, options: [])) as?

[String: Any], let result = Weather(jsonDictionary: json) { fulfill(result) } else if let error = error { reject(error) } else { let error = NSError(domain: "PromiseKitTutorial", code: 0, userInfo: [NSLocalizedDescriptionKey: "Unknown error"]) reject(error) } } dataTask.resume() } }

這種方法和 getWeatherTheOldFashionedWay 方法同樣也使用 URLSession,但沒有使用完畢塊,而是將網絡操做放在了一個 Promise 中。

當 dataTask 的 completion 處理回調中,假設成功返回數據,將 JSON 序列化後建立一個 Weather 對象。

用這個對象調用 fulfill 函數。完畢這個承諾。

假設發生錯誤。用 error 對象調用 reject 函數。

不然。代表既沒有返回 JSON 數據也沒有發生錯誤,則建立一個 NSError 傳遞給 reject 函數,因爲調用 reject 函數必需要一個 NSError 參數。

而後,在 WeatherViewController.swift 中將 handleLocation(city:state:latitude:longitude:) 替換成:

func handleLocation(city: String?, state: String?,
                    latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
  if let city = city, let state = state {
    self.placeLabel.text = "\(city), \(state)"
  }

  weatherAPI.getWeather(latitude: latitude, longitude: longitude).then { weather -> Void in
    self.updateUIWithWeather(weather: weather)

    }.catch { error in
      self.tempLabel.text = "--"
      self.conditionLabel.text = error.localizedDescription
      self.conditionLabel.textColor = errorColor
  }
}

太棒了,使用 promise 時僅僅需要提供一個 then 塊和一個 catch 塊!

新的 handleLocation 方法和原來相比。好了不少。

首先。單一的完畢塊被分爲兩個可讀性更好的塊:then 用於成功 catch 用於失敗。

其次。默認 PromiseKit 在主線程中運行這兩個塊,所以不會致使在後臺線程中刷新 UI 的發生錯誤。

PromiseKit Wrapper

Promise 很是好。但 PromiseKit 並不只僅是這些。

除了 Promise,PromiseKit 還對常見的 iOS SDK 方法進行了擴展,讓它們可以以承諾的方法表達。

好比。URLSession data task 方法的完畢塊可以替換爲 promise。

將 getWeather(latitude:longitude:) 方法替換爲:

func getWeather(latitude: Double, longitude: Double) -> Promise<Weather> {
  return Promise { fulfill, reject in
    let urlString = "http://api.openweathermap.org/data/2.5/weather?lat=" +
                    "\(latitude)&lon=\(longitude)&appid=\(appID)"
    let url = URL(string: urlString)!
    let request = URLRequest(url: url)

    let session = URLSession.shared

    // 1
    let dataPromise: URLDataPromise = session.dataTask(with: request)

    // 2
    _ = dataPromise.asDictionary().then { dictionary -> Void in

      // 3
      guard let result = Weather(jsonDictionary: dictionary as! [String : Any]) else {
        let error = NSError(domain: "PromiseKitTutorial", code: 0,
                            userInfo: [NSLocalizedDescriptionKey: "Unknown error"])
        reject(error)
        return
      }

      fulfill(result)

      // 4
      }.catch(execute: reject)
  }
}

看到了嗎?PromiseKit 的 Wrapper 就這麼簡單!解釋一下上述代碼:

  1. PromiseKit 提供了一個 URLSession.dataTask(with:) 方法的重載,讓它返回一個 URLDataPromise,這是一個類型化的 Promise。注意,data promise 本身主動啓動它所包括的 data task。

  2. 所返回的這個 dataPromise 有一個便利方法 asDictionary(),這種方法會爲你處理 JSON 的序列化,可以大大節省你的代碼!
  3. 因爲已經解析好 dictionary,可以直接用它建立一個 result 對象。咱們用 guard let 語句確保從 dictionary 建立 Weather 對象必定成功。假設不,建立一個 NSError 並調用 reject 函數,和前面同樣。不然,用 result 對象調用 fulfill 函數。
  4. 在這個過程當中。有可能網絡請求失敗。或者 JSON 序列化失敗。

    在以前的方法中咱們必須分別檢查這兩種狀況。而這裏。僅僅需要一個 catch 塊就能讓所有錯誤進入失敗塊。

在這種方法中,兩個 promise 被連接在一塊兒。

第一個 promise 是 dataPromise。它從 URL 請求中返回數據 data。第二個 promise 是 asDictionary()。它用 data 作參數並將它轉換成字典返回。

加入地點

現在網絡部分已經就緒,咱們來看單位功能。無論你是否有幸去過希臘,這個 app 都不會給你真正想要的數據。要解決這個,咱們需要使用設備的定位功能。

在 WeatherViewController.swift 中,將 updateWithCurrentLocation() 替換爲:

private func updateWithCurrentLocation() {
  // 1
  _ = locationHelper.getLocation().then { placemark in
    self.handleLocation(placemark: placemark)
    }.catch { error in
      self.tempLabel.text = "--"
      self.placeLabel.text = "--"
      switch error {

      // 2
      case is CLError where (error as! CLError).code == CLError.Code.denied:
        self.conditionLabel.text = "Enable Location Permissions in Settings"
        self.conditionLabel.textColor = UIColor.white
      default:
        self.conditionLabel.text = error.localizedDescription
        self.conditionLabel.textColor = errorColor
      }
  }
}

這裏使用了輔助類來進行 Core Location 調用。待會再來實現這個類。getLocation() 返回一個 promise。這個 promise 會從當前位置得到一個地名 placemark。

這個 catch 塊顯示了各類錯誤並在單個 catch 塊中對錯誤進行處理。用一個 switch 語句,依據用戶是否授予位置訪問權限仍是其餘類型的錯誤來給予不一樣的提示。

而後,在 LocationHelper.swift 中將 getLocation() 替換爲:

func getLocation() -> Promise<CLPlacemark> {
  // 1
  return CLLocationManager.promise().then { location in

    // 2
    return self.coder.reverseGeocode(location: location)
  }
}

這裏利用了前面介紹過的 PromiseKit 的兩個概念:PromiseKit Wrapper 和 promise 鏈。

CLLocationManager.promise() 返回了一個當前位置的 promise。

一旦獲取到用戶當前位置,將位置傳遞給 CLGeocoder.reverseGeocode(location:), 方法,這也返回了一個 promise。返回反地理編碼的位置。

經過 promise。兩個異步動做被連接在 3 行代碼裏。因爲所有的錯誤處理都由調用者的 catch 塊處理,咱們也不需要顯式的異常處理。

運行 app。接受地理位置受權請求。你當前位置(模擬器)的溫度顯示了。成功了。

https://koenig-media.raywenderlich.com/uploads/2016/10/2_build_and_run_with_location.png

搜索其餘位置

幹得不錯,但用戶想知道其餘地方的氣溫怎麼辦?

在 WeatherViewController.swift 中將 textFieldShouldReturn(_:) 替換爲(臨時不用管編譯器報的「missing method」錯誤):

func textFieldShouldReturn(_ textField: UITextField) -> Bool {

  textField.resignFirstResponder()

  guard let text = textField.text else { return true }
  _ = locationHelper.searchForPlacemark(text: text).then { placemark -> Void in
    self.handleLocation(placemark: placemark)
  }
  return true
}

這裏使用了和其餘幾個 promise 的一樣模板:查找地名,找到後刷新 UI。

而後,在 LocationHelper.swift 加入方法:

func searchForPlacemark(text: String) -> Promise<CLPlacemark> {
  return CLGeocoder().geocode(text)
}

很是easy!PromiseKit 已經對 CLGeocoder 進行了擴展,會查找匹配的 placemark 並用一個 promise 返回 placemark。

運行 app,此次在頂部搜索欄中輸入一個城市名稱後點擊回車。這回找到一個最匹配的城市名並獲取天氣信息。

線程

咱們已經習慣了將所有的塊都放在主線程中運行。這是一個很是好的特性。因爲 view controller 中大部分工做都和刷新 UI 有關。但是,對於耗時任務,應當在後臺線程中進行。這樣不會堵塞 app。

咱們接下來會從 OpenWeatherMap 載入一個圖標,以表示當前的天氣情況。

在 WeatherHelper 的 getWeather(latitude:longitude:) 方法後加入這種方法:

func getIcon(named iconName: String) -> Promise<UIImage> {
  return Promise { fulfill, fail in
    let urlString = "http://openweathermap.org/img/w/\(iconName).png"
    let url = URL(string: urlString)!
    let request = URLRequest(url: url)

    let session = URLSession.shared
    let dataPromise: URLDataPromise = session.dataTask(with: request)
    let backgroundQ = DispatchQueue.global(qos: .background)
    _ = dataPromise.then(on: backgroundQ) { data -> Void in
      let image = UIImage(data: data)!
      fulfill(image)
      }.catch(execute: fail)
  }
}

這裏。咱們在 then(on:execute:) 方法中用 on 參數指定圖片的載入在後臺隊列中進行。PromiseKit 會將繁重任務放到指定的 dispatch 中進行。

現在。promise 在後臺隊列中被兌現,這樣調用者就必須本身保證 UI 刷新在主隊列中進行了。
回到 WeatherViewController.swift。在 handleLocation(city:state:latitude:longitude:) 方法中,將調用 getWeather(latitude:longitude:) 的語句改動爲:

// 1
weatherAPI.getWeather(latitude: latitude, longitude: longitude).then { weather -> Promise<UIImage> in
  self.updateUIWithWeather(weather: weather)

  // 2
  return self.weatherAPI.getIcon(named: weather.iconName)

  // 3
  }.then(on: DispatchQueue.main) { icon -> Void in
    self.iconImageView.image = icon

  }.catch { error in
    self.tempLabel.text = "--"
    self.conditionLabel.text = error.localizedDescription
    self.conditionLabel.textColor = errorColor
}

在這個調用中。有 3 個地方與以前有細微的差異:

  1. 首先,getWeather(latitude:longitude:) 的 then 塊的返回值由原來的 Void 改動 promise。這意味着當 getWeather 的承諾兌現時, 又給出了還有一個承諾。

  2. 用 getIco 方法建立一個新的承諾…以獲得一個圖標。
  3. 在承諾鏈中再加一個 then 塊,當 getIcon 被兌現時,這個塊要在主線程中運行。

這樣,承諾以一種順序運行的步驟連接在一塊兒。當一個承諾兌現。下一個承諾會被運行時,以此類推直到最後一個 then 或者有發生錯誤——即 catch 塊被調用。這樣的方式比起嵌套完畢塊來講有兩大優勢:

  1. 承諾以單鏈的形式構建,易於閱讀和維護。

    每個 then 塊都有單獨的上下文,避免邏輯和狀態相互污染。豎列的代碼塊不需要很是多縮進,讀起來更加輕鬆。

  2. 所有錯誤代碼都在一個地方進行處理。好比。在一個複雜的流程中。比方用戶登陸,僅僅需要一個錯誤對話框就可以顯示每個步驟所發生的錯誤。

運行 app。圖片顯示了!

封裝承諾

怎樣調用不支持 PromiseKit 的老代碼、SDK 或者第三方庫?PromiseKit 有一個 promise wrapper。

以咱們的 APP 爲例。因爲天氣情況老是有限的。沒有必要每次都從 web 抓取表示天氣情況的圖片,不但效率低下,而且會形成浪費。
在 WeatherHelper.swift 已經有一個輔助方法,將圖片載入並保存到本地緩存中。

這些函數在後臺線程中進行文件 IO 操做,當操做完畢時調用異步完畢塊。

這是最普通的方法,PromiseKit 提供了一種替代方法。

將 WeatherHelper 中的 getIcon(named:) 替換爲(一樣, 臨時忽略編譯器的報警):

func getIcon(named iconName: String) -> Promise<UIImage> {

  // 1
  return wrap {
    // 2
    getFile(named: iconName, completion: $0)

    } .then { image in
      if image == nil {
        // 3
        return self.getIconFromNetwork(named: iconName)

      } else {
        // 4
        return Promise(value: image!)
      }
  }
}

代碼解釋例如如下:

  1. wrap(body:) 可以將跟在多個完畢塊後面的某個函數封裝成一個承諾。

  2. getFile(named: completion:) 有一個完畢塊參數 @escaping (UIImage?

    ) -> Void, 它會被轉換成一個 Promise。

    在 wrap 的塊中,調用了這個函數,將完畢塊參數傳入。

  3. 假設圖片未緩存到本地。返回一個承諾,從網絡抓取圖片。
  4. 假設圖片緩存有效,返回一個值承諾(value promise)。

這是一種新的 promise 的使用方法。假設建立承諾時使用一個已經兌現的值。將立刻調用 then 塊。這樣,假設圖片已經在本地,它會立刻返回。

這樣的方式既可以建立一個承諾去異步運行某件事情(比方從網絡載入),也能同步運行某件事情(比方使用一個內存中的值)。這在你有本地緩存時是實用的,比方這裏的圖片。

要讓上述代碼可以工做,咱們必須在獲取到圖片時對它進行緩存。在前面的方法後面加入方法:

func getIconFromNetwork(named iconName: String) -> Promise<UIImage> {
  let urlString = "http://openweathermap.org/img/w/\(iconName).png"
  let url = URL(string: urlString)!
  let request = URLRequest(url: url)

  let session = URLSession.shared
  let dataPromise: URLDataPromise = session.dataTask(with: request)
  return dataPromise.then(on: DispatchQueue.global(qos: .background)) { data -> Promise<UIImage> in
    return firstly { Void in
      return wrap { self.saveFile(named: iconName, data: data, completion: $0)}
      }.then { Void -> Promise<UIImage> in
        let image = UIImage(data: data)!
        return Promise(value: image)
    }
  }
}

和先前的 getIcon(named:)方法同樣。但是在 dataPromise 的 then 塊中調用了 saveFile 方法,這種方法進行了和 getFile 方法同樣的封裝。

這裏用到了一個新結構,firstly。

firstly 是一個語法糖。簡單滴運行它的承諾。事實上僅僅是加入了一層封裝以便更易讀。因爲 saveFile 方法調用是載入圖標後的一個附帶功能。用 firstly 可以確保運行的順序以便咱們可以對這個承諾更有信心一點。

當你第一次請求圖片時會是這個樣子:

  1. 首先,載入一個 URLRequest。
  2. 載入成功後。數據保存到文件裏。

  3. 保存完後,將數據轉換成圖片傳遞給下個承諾鏈。

假設你運行 app,不會有什麼不一樣,但經過文件系統你可以看到圖片都被保存了。你可以在控制檯中搜索 Save iamge to:,它會顯示文件保存的 URL 地址,你可以在硬盤上找到這個文件:

確認動做

看過了 PromiseKit 的語法,你可能會問:既然有 then 和 catch,那麼有 finally 嗎(比方運行一些清理),確保某些動做老是會發生,而無論是否成功?答案是:always。

在 WeatherViewController.swift 中改動handleLocation(city:state:latitude:longitude:),當從server抓取天氣數據時。在狀態欄中顯示一個小菊花。

在調用 weatherAPI.getWeather… 以前插入代碼:

UIApplication.shared.isNetworkActivityIndicatorVisible = true

而後。在 catch 塊後面加入:

.always {
  UIApplication.shared.isNetworkActivityIndicatorVisible = false
}

而後,爲了讓編譯器再也不報「unused result」警告。將整個表達式賦給一個 _。

這是 always 的一個常規使用方法。無論是載入成功仍是出錯,以及網絡活動是否完畢,網絡活動狀態都應當隱藏。

類似的,可以用 always 關閉 socket。數據庫鏈接或者斷開硬件服務。

定時器

有一種例外狀況。即承諾會在數據有效並通過某個固定時間週期以後纔會兌現。

當前。當天氣信息載入後就不會刷新了。咱們可以改動它,讓它每隔一個小時就更新一次。
在 updateWithCurrentLocation() 方法最後加入代碼:

_ = after(interval: oneHour).then {
  self.updateWithCurrentLocation()
}

.after(interval:) 建立一個承諾,以指定時間間隔兌現。不幸的是,這是一個一次性的定時器。要每小時刷新一次,需要在 updateWithCurrentLocation() 中遞歸。

並列承諾

當前的所有承諾都是孤立或者以某種順序鏈式運行。PromiseKit 也提供了一個功能,將多個承諾同一時候兌現。

有 3 個函數用於等待多個承諾。第一個 race。當一堆承諾兌現時返回第一個承諾。

也就是,第一個完畢的勝出。


另外兩個函數是 when 和 join。它們都是在指定的承諾被兌現以後調用。僅僅是 rejected 塊有所不一樣。join 在拒絕以前老是等待所有的承諾完畢。看它們之中是否有被拒絕的。

而 when(fulfilled:) 僅僅要有不論什麼一個承諾被拒絕它就拒絕。另外。when(resolved:) 會等待所有的承諾完畢,但 then 塊總會調用,catch 塊永遠不會調用。

注:對於所有的聚合函數,所有的單一承諾都會繼續指導它們要麼兌現要麼拒絕,無論聚合函數的行爲是什麼。好比,假設三個承諾使用了 race 函數,當第一個承諾完畢時,then 塊被調用。

但是其餘兩個未知足的承諾仍然會繼續運行,一直到它們也被解決。

以隨機顯示隨意城市的天氣爲例。因爲用戶不知道會顯示什麼城市,app 一次會抓取多個城市,但它僅僅處理第一個城市。

這會形成一種隨機的假象。

將 showRandomWeather(_:) 替換爲:

@IBAction func showRandomWeather(_ sender: AnyObject) {
  let weatherPromises = randomCities.map { weatherAPI.getWeather(latitude: $0.2, longitude: $0.3) }
  _ = race(promises: weatherPromises).then { weather -> Void in
    self.placeLabel.text = weather.name
    self.updateUIWithWeather(weather: weather)
    self.iconImageView.image = nil
  }
}

這裏咱們建立了多個承諾去抓取城市列表中的天氣。這些承諾用 race(promises:) 函數造成進行競爭關係。僅僅有第一個被知足的承諾會調用 then 塊。理論上,這是一種隨機選擇,因爲server情況是不定的,但這個樣例的說服力不是很是強。注意所有的承諾都會繼續運行,所以仍然會有 5 個網絡調用,固然僅僅有一個承諾會被關心。

運行 app。當 app 啓動,點擊 Random Weather。

關於天氣圖標的刷新和錯誤處理就留給讀者練練手了 ;]

結束

這裏下載最後完畢項目。
請在閱讀 PromiseKit 文檔:http://promisekit.org/,雖然它看起來很是難。FAQ http://promisekit.org/faq/ 對於調試信息很是有幫助。


PromiseKit 是一個活躍的 pod,爲了在本身的項目中安裝 cocoapods,並保持它的更新,你可能需要研究一下 CocoaPods 的使用方法

最後說一句,Promise 還有其餘 Swift 實現。當中一個流行的實現就是 BrightFutures。 假設你有不論什麼建議、問題和評論,請在如下留言。

相關文章
相關標籤/搜索