全部文章
以下是對
PromiseKit的README.md的翻譯。
錯誤的使用
多重包含 Promise
錯誤的示例:
func toggleNetworkSpinnerWithPromise<T>(funcToCall: () -> Promise<T>) -> Promise<T> {
return Promise { seal in
firstly {
setNetworkActivityIndicatorVisible(true)
return funcToCall()
}.then { result in
seal.fulfill(result)
}.always {
setNetworkActivityIndicatorVisible(false)
}.catch { err in
seal.reject(err)
}
}
}
修改為:
func toggleNetworkSpinnerWithPromise<T>(funcToCall: () -> Promise<T>) -> Promise<T> {
return firstly {
setNetworkActivityIndicatorVisible(true)
return funcToCall()
}.always {
setNetworkActivityIndicatorVisible(false)
}
}
在已有的 promise 上,不需要再使用一個 promise 去包裹它。
Promise 中的可選
當你看到 Promise<Item?> 時,很有可能用錯了 Promise。比如:
return firstly {
getItems()
}.then { items -> Promise<[Item]?> in
guard !items.isEmpty else {
return .value(nil)
}
return Promise(value: items)
}
有時也會出現(xiàn)在第二個 then 中會返回 nil。這就將檢查 nil 的工作交給了 promise 的調用者。
通常,更好的做法是將這些異常情況當成錯誤來處理,而不是把它當前正常情況來對待。對于這種情況,你可以創(chuàng)建一個特殊的錯誤類型來表示這種情況。
return firstly {
getItems()
}.map { items -> [Item]> in
guard !items.isEmpty else {
throw MyError.emptyItems
}
return items
}
注意:當別人的 API 返回一個可選項時,你可以通過使用
compactMap將 nil 轉化為 error。
技巧和竅門
后臺加載成員變量
class MyViewController: UIViewController {
private let ambience: Promise<AVAudioPlayer> = DispatchQueue.global().async(.promise) {
guard let asset = NSDataAsset(name: "CreepyPad") else { throw PMKError.badInput }
let player = try AVAudioPlayer(data: asset.data)
player.prepareToPlay()
return player
}
}
<a name="WPj42"></a>
鏈接動畫
firstly {
UIView.animate(.promise, duration: 0.3) {
self.button1.alpha = 0
}
}.then {
UIView.animate(.promise, duration: 0.3) {
self.button2.alpha = 1
}
}.then {
UIView.animate(.promise, duration: 0.3) {
adjustConstraints()
self.view.layoutIfNeeded()
}
}
將 Promise 的結果轉化為 void
有時候抹除 promise 的執(zhí)行結果后很方便。比如由于 UIKit 執(zhí)行結束后會返回一個 bool 值,所以 UIView.animate(.promise) 返回的類型為 Guarantee<Bool>。但是,我們通常不需要這個返回值,而且將這個返回值丟棄后(即將結果轉化為 void),會讓調用鏈變得更加簡單。此時,我們可以使用 asVoid() 進行轉化:
UIView.animate(.promise, duration: 0.3) {
self.button1.alpha = 0
}.asVoid().done(self.nextStep)
當我們用 when 組合多個 promise 時, asVoid() 就變得非常有用:
let p1 = foo()
let p2 = bar()
let p3 = baz()
//…
let p10 = fluff()
when(fulfilled: p1.asVoid(), p2.asVoid(), /*…*/, p10.asVoid()).then {
let value1 = p1.value! // safe bang since all the promises fulfilled
// …
let value10 = p10.value!
}.catch {
//…
}
你基本遇不到這么多參數(shù)的情況,所以 when 最多提供了 5 個參數(shù)
阻塞(等待)
有時在等待異步任務完成時,你需要阻塞主線程。這時,你可以(謹慎)使用 wait :
public extension UNUserNotificationCenter {
var wasPushRequested: Bool {
let settings = Guarantee(resolver: getNotificationSettings).wait()
return settings != .notDetermined
}
}
Promise 的內部任務的回調不能在當前線程上執(zhí)行,否則將會死鎖。
在 Background 線程/隊列上執(zhí)行調用鏈
first 中故意沒有設計 queue 參數(shù)。這樣做的愿意可以查看 ticket tracker。
所以,當你希望在 Background 隊列中執(zhí)行一個調用鏈時,你必須使用 DispatchQueue.async :
DispatchQueue.global().async(.promise) {
return value
}.done { value in
//…
}
由于 Swift 編譯器類型推斷上的問題,所以這個函數(shù)無法返回 Promise。因此,當你在后臺隊列開始運行 Promise 時,需要執(zhí)行以下操作:
Promise { seal in
DispatchQueue.global().async {
seal(value)
}
}.done { value in
//…
}
或者更簡單(盡管有警告,請查看 wait 文檔):
DispatchQueue.global().async(.promise) {
return try fetch().wait()
}.done { value in
//…
}
但是,你應該不會經(jīng)常這樣寫。當你想用這種技術的時候,你或許應該修改 fetch 中的代碼,來使它運行在后臺線程上。
Promise 抽象異步操作,所以支持并實現(xiàn)了這種模式。在設計 API 時,你不應該讓使用者去操心你提供的方法到底運行在什么隊列上。