PromiseKit 附錄

全部文章

以下是對(duì) PromiseKitREADME.md 的翻譯。

錯(cuò)誤的使用

多重包含 Promise

錯(cuò)誤的示例:

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 上,不需要再使用一個(gè) promise 去包裹它。

Promise 中的可選

當(dāng)你看到 Promise<Item?> 時(shí),很有可能用錯(cuò)了 Promise。比如:

return firstly {
    getItems()
}.then { items -> Promise<[Item]?> in
    guard !items.isEmpty else {
        return .value(nil)
    }
    return Promise(value: items)
}

有時(shí)也會(huì)出現(xiàn)在第二個(gè) then 中會(huì)返回 nil。這就將檢查 nil 的工作交給了 promise 的調(diào)用者。

通常,更好的做法是將這些異常情況當(dāng)成錯(cuò)誤來(lái)處理,而不是把它當(dāng)前正常情況來(lái)對(duì)待。對(duì)于這種情況,你可以創(chuàng)建一個(gè)特殊的錯(cuò)誤類型來(lái)表示這種情況。

return firstly {
    getItems()
}.map { items -> [Item]> in
    guard !items.isEmpty else {
        throw MyError.emptyItems
    }
    return items
}

注意:當(dāng)別人的 API 返回一個(gè)可選項(xiàng)時(shí),你可以通過(guò)使用 compactMap 將 nil 轉(zhuǎn)化為 error。

技巧和竅門(mén)

后臺(tái)加載成員變量

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>

鏈接動(dòng)畫(huà)

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 的結(jié)果轉(zhuǎn)化為 void

有時(shí)候抹除 promise 的執(zhí)行結(jié)果后很方便。比如由于 UIKit 執(zhí)行結(jié)束后會(huì)返回一個(gè) bool 值,所以 UIView.animate(.promise) 返回的類型為 Guarantee<Bool>。但是,我們通常不需要這個(gè)返回值,而且將這個(gè)返回值丟棄后(即將結(jié)果轉(zhuǎn)化為 void),會(huì)讓調(diào)用鏈變得更加簡(jiǎn)單。此時(shí),我們可以使用 asVoid() 進(jìn)行轉(zhuǎn)化:

UIView.animate(.promise, duration: 0.3) {
    self.button1.alpha = 0
}.asVoid().done(self.nextStep)

當(dāng)我們用 when 組合多個(gè) promise 時(shí), 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 個(gè)參數(shù)

阻塞(等待)

有時(shí)在等待異步任務(wù)完成時(shí),你需要阻塞主線程。這時(shí),你可以(謹(jǐn)慎)使用 wait

public extension UNUserNotificationCenter {
    var wasPushRequested: Bool {
        let settings = Guarantee(resolver: getNotificationSettings).wait()
        return settings != .notDetermined
    }
}

Promise 的內(nèi)部任務(wù)的回調(diào)不能在當(dāng)前線程上執(zhí)行,否則將會(huì)死鎖。

在 Background 線程/隊(duì)列上執(zhí)行調(diào)用鏈

first 中故意沒(méi)有設(shè)計(jì) queue 參數(shù)。這樣做的愿意可以查看 ticket tracker。

所以,當(dāng)你希望在 Background 隊(duì)列中執(zhí)行一個(gè)調(diào)用鏈時(shí),你必須使用 DispatchQueue.async

DispatchQueue.global().async(.promise) {
    return value  
}.done { value in
    //…
}

由于 Swift 編譯器類型推斷上的問(wèn)題,所以這個(gè)函數(shù)無(wú)法返回 Promise。因此,當(dāng)你在后臺(tái)隊(duì)列開(kāi)始運(yùn)行 Promise 時(shí),需要執(zhí)行以下操作:

Promise { seal in
    DispatchQueue.global().async {
        seal(value)
    }  
}.done { value in
    //…
}

或者更簡(jiǎn)單(盡管有警告,請(qǐng)查看 wait 文檔):

DispatchQueue.global().async(.promise) {
    return try fetch().wait()
}.done { value in
    //…
}

但是,你應(yīng)該不會(huì)經(jīng)常這樣寫(xiě)。當(dāng)你想用這種技術(shù)的時(shí)候,你或許應(yīng)該修改 fetch 中的代碼,來(lái)使它運(yùn)行在后臺(tái)線程上。

Promise 抽象異步操作,所以支持并實(shí)現(xiàn)了這種模式。在設(shè)計(jì) API 時(shí),你不應(yīng)該讓使用者去操心你提供的方法到底運(yùn)行在什么隊(duì)列上。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容