CancelForPromiseKit
API 文档 | CancelForPromiseKit | CPKAlamofire | CPKCoreLocation | CPKFoundation |
---|
CancelForPromiseKit 为最优秀的 PromiseKit 和 PromiseKit 扩展 提供了清晰简洁的取消功能。虽然 PromiseKit 包含基本的取消支持,但 CancelForPromiseKit 将其扩展为使取消承诺及其相关任务变得简单直接。
例如
UIApplication.shared.isNetworkActivityIndicatorVisible = true
let fetchImage = URLSession.shared.dataTaskCC(.promise, with: url).compactMap{ UIImage(data: $0.data) }
let fetchLocation = CLLocationManager.requestLocationCC().lastValue
let context = firstly {
when(fulfilled: fetchImage, fetchLocation)
}.done { image, location in
self.imageView.image = image
self.label.text = "\(location)"
}.ensure {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}.catch(policy: .allErrors) { error in
/* Will be invoked with a PromiseCancelledError when cancel is called on the context.
Use the default policy of .allErrorsExceptCancellation to ignore cancellation errors. */
self.show(UIAlertController(for: error), sender: self)
}.cancelContext
//…
// Cancel currently active tasks and reject all cancellable promises with PromiseCancelledError
context.cancel()
/* Note: Cancellable promises can be cancelled directly. However by holding on to
the CancelContext rather than a promise, each promise in the chain can be
deallocated by ARC as it is resolved. */
注意:本README及整个项目的格式旨在保持可读性和简洁性,与 PromiseKit 保持一致。对所有代码示例,PromiseKit 和 CancelForPromiseKit 之间的差异以粗体显示。
用CocoaPods快速入门
在你的 Podfile
use_frameworks!
target "Change Me!" do
pod "PromiseKit", "~> 6.0"
pod "CancelForPromiseKit", "~> 1.0"
end
CancelForPromiseKit 与 PromiseKit 具有相同的平台和 Xcode 支持
例子
- 取消一个链
let promise = firstly {
/* Methods and functions with the CC (a.k.a. cancel chain) suffix initiate a
cancellable promise chain by returning a CancellablePromise. */
loginCC()
}.then { creds in
/* 'fetch' in this example may return either Promise or CancellablePromise --
If 'fetch' returns a CancellablePromise then the fetch task can be cancelled.
If 'fetch' returns a standard Promise then the fetch task cannot be cancelled,
however if cancel is called during the fetch then the promise chain will still be
rejected with a PromiseCancelledError as soon as the 'fetch' task completes.
Note: if 'fetch' returns a CancellablePromise then the convention is to name
it 'fetchCC'. */
fetch(avatar: creds.user)
}.done { image in
self.imageView = image
}.catch(policy: .allErrors) { error in
if error.isCancelled {
// the chain has been cancelled!
}
}
// …
/* 'promise' here refers to the last promise in the chain. Calling 'cancel' on
any promise in the chain cancels the entire chain. Therefore cancelling the
last promise in the chain cancels everything.
Note: It may be desirable to hold on to the CancelContext directly rather than a
promise so that the promise can be deallocated by ARC when it is resolved. */
promise.cancel()
- 将 Promise 和 CancellablePromise 混合以取消某些分支而不取消其他分支
在上面的例子中:如果 fetch(avatar: creds.user)
返回一个标准 Promise,那么检索不能被取消。然而,如果在检索过程中调用 cancel,那么在检索完成时,promise chain 仍然会因 PromiseCancelledError 被拒绝。不会调用 done
块,而是调用 catch(policy: .allErrors)
块。
如果 fetch
返回一个 CancellablePromise,那么当调用 cancel()
时,检索将被取消,并且立即调用 catch
块。
- 使用 'delegate' promise
CancellablePromise 包装了一个 delegate Promise,可以通过 promise
属性访问。上面示例可以修改如下,从而使 'loginCC' 完成后,链不能再取消
let cancellablePromise = firstly {
loginCC()
}
cancellablePromise.then { creds in
// For this example 'fetch' returns a standard Promise
fetch(avatar: creds.user)
/* Here, by calling 'promise.done' rather than 'done' the chain is converted from a
cancellable promise chain to a standard Promise chain. In this case, calling
'cancel' during the 'fetch' operation has no effect: */
}.promise.done { image in
self.imageView = image
}.catch(policy: .allErrors) { error in
if error.isCancelled {
// the chain has been cancelled!
}
}
// …
cancellablePromise.cancel()
文档
以下函数和方法是 CancelForPromiseKit 核心模块的一部分。带有 CC 后缀的函数和方法创建一个新的 CancellablePromise,而没有带有 CC 后缀的函数和方法接受现有的 CancellablePromise。
以下是 Jazzy 生成的 CancelForPromiseKit API 文档。
Global functions (all returning CancellablePromise unless otherwise noted)
afterCC(seconds:)
afterCC(_ interval:)
firstly(execute body:) // Accepts body returning CancellableTheanble
firstlyCC(execute body:) // Accepts body returning Theanble
hang(_ promise:) // Accepts CancellablePromise
race(_ thenables:) // Accepts CancellableThenable
race(_ guarantees:) // Accepts CancellableGuarantee
raceCC(_ thenables:) // Accepts Theanable
raceCC(_ guarantees:) // Accepts Guarantee
when(fulfilled thenables:) // Accepts CancellableThenable
when(fulfilled promiseIterator:concurrently:) // Accepts CancellablePromise
whenCC(fulfilled thenables:) // Accepts Thenable
whenCC(fulfilled promiseIterator:concurrently:) // Accepts Promise
// These functions return CancellableGuarantee
when(resolved promises:) // Accepts CancellablePromise
when(_ guarantees:) // Accepts CancellableGuarantee
when(guarantees:) // Accepts CancellableGuarantee
whenCC(resolved promises:) // Accepts Promise
whenCC(_ guarantees:) // Accepts Guarantee
whenCC(guarantees:) // Accepts Guarantee
CancellablePromise: CancellableThenable
CancellablePromise.value(_ value:)
init(task:resolver:)
init(task:bridge:)
init(task:error:)
promise // The delegate Promise
result
CancellableGuarantee: CancellableThenable
CancellableGuarantee.value(_ value:)
init(task:resolver:)
init(task:bridge:)
init(task:error:)
guarantee // The delegate Guarantee
result
CancellableThenable
thenable // The delegate Thenable
cancel(error:) // Accepts optional Error to use for cancellation
cancelContext // CancelContext for the cancel chain we are a member of
isCancelled
cancelAttempted
cancelledError
appendCancellableTask(task:reject:)
appendCancelContext(from:)
then(on:_ body:) // Accepts body returning CancellableThenable or Thenable
map(on:_ transform:)
compactMap(on:_ transform:)
done(on:_ body:)
get(on:_ body:)
asVoid()
error
isPending
isResolved
isFulfilled
isRejected
value
mapValues(on:_ transform:)
flatMapValues(on:_ transform:)
compactMapValues(on:_ transform:)
thenMap(on:_ transform:) // Accepts transform returning CancellableThenable or Thenable
thenFlatMap(on:_ transform:) // Accepts transform returning CancellableThenable or Thenable
filterValues(on:_ isIncluded:)
firstValue
lastValue
sortedValues(on:)
CancellableCatchable
catchable // The delegate Catchable
recover(on:policy:_ body:) // Accepts body returning CancellableThenable or Thenable
recover(on:_ body:) // Accepts body returning Void
ensure(on:_ body:)
ensureThen(on:_ body:)
finally(_ body:)
cauterize()
扩展
只要底层的异步任务支持取消,CancelForPromiseKit 提供与 PromiseKit 相同的扩展和功能
默认 CocoaPod 提供了核心可取消的承诺和 Foundation 扩展。其他扩展通过在您的 Podfile
中指定附加 subspec 可用,例如
pod "CancelForPromiseKit/MapKit"
# MKDirections().calculateCC().then { /*…*/ }
pod "CancelForPromiseKit/CoreLocation"
# CLLocationManager.requestLocationCC().then { /*…*/ }
与PromiseKit相同,所有扩展都是独立的仓库。以下是所有CancelForPromiseKit扩展的完整列表,列出了支持取消的具体功能(没有支持取消功能的PromiseKit扩展已省略)
Alamofire.DataRequest
responseCC(_:queue:)
responseDataCC(queue:)
responseStringCC(queue:)
responseJSONCC(queue:options:)
responsePropertyListCC(queue:options:)
responseDecodableCC(queue::decoder:)
responseDecodableCC(_ type:queue:decoder:)
Alamofire.DownloadRequest
responseCC(_:queue:)
responseDataCC(queue:)
Bolts
Cloudkit
CoreLocation
Foundation
Process
launchCC(_:)
URLSession
dataTaskCC(_:with:)
uploadTaskCC(_:with:from:)
uploadTaskCC(_:with:fromFile:)
downloadTaskCC(_:with:to:)
MapKit
OMGHTTPURLRQ
StoreKit
WatchConnectivity
我不想安装扩展!
与PromiseKit一样,扩展是可选的
pod "CancelForPromiseKit/CorePromise", "~> 1.0"
注意 使用Carthage安装的默认情况下不带任何扩展。
选择您的网络库
使用PromiseKit支持的所有的网络库扩展现在都可以简单地取消操作了!
// pod 'CancelForPromiseKit/Alamofire'
// # https://github.com/dougzilla32/CancelForPromiseKit-Alamofire
let context = firstly {
Alamofire
.request("http://example.com", method: .post, parameters: params)
.responseDecodableCC(Foo.self, cancel: context)
}.done { foo in
//…
}.catch { error in
//…
}.cancelContext
//…
context.cancel()
// pod 'CancelForPromiseKit/OMGHTTPURLRQ'
// # https://github.com/dougzilla32/CancelForPromiseKit-OMGHTTPURLRQ
let context = firstly {
URLSession.shared.POSTCC("http://example.com", JSON: params)
}.map {
try JSONDecoder().decoder(Foo.self, with: $0.data)
}.done { foo in
//…
}.catch { error in
//…
}.cancelContext
//…
context.cancel()
当然,还有从Foundation中的平
凡URLSession
// pod 'CancelForPromiseKit/Foundation'
// # https://github.com/dougzilla32/CancelForPromiseKit-Foundation
let context = firstly {
URLSession.shared.dataTaskCC(.promise, with: try makeUrlRequest())
}.map {
try JSONDecoder().decode(Foo.self, with: $0.data)
}.done { foo in
//…
}.catch { error in
//…
}.cancelContext
//…
context.cancel()
func makeUrlRequest() throws -> URLRequest {
var rq = URLRequest(url: url)
rq.httpMethod = "POST"
rq.addValue("application/json", forHTTPHeaderField: "Content-Type")
rq.addValue("application/json", forHTTPHeaderField: "Accept")
rq.httpBody = try JSONSerialization.jsonData(with: obj)
return rq
}
设计目标
- 提供了一种简化的方式来取消Promise链,这会拒绝所有关联的Promise并取消所有关联的任务。例如
let promise = firstly {
loginCC() // Use CC (a.k.a. cancel chain) methods or CancellablePromise to
// initiate a cancellable promise chain
}.then { creds in
fetch(avatar: creds.user)
}.done { image in
self.imageView = image
}.catch(policy: .allErrors) { error in
if error.isCancelled {
// the chain has been cancelled!
}
}
//…
promise.cancel()
-
确保Promise链被取消后,后续的代码块永远不会被调用
-
完全支持并发,确保所有代码都是线程安全的
-
为所有适当的PromiseKit扩展(例如 Foundation、CoreLocation、Alamofire等)提供可取消的变体
-
支持所有的PromiseKit原语(例如 'after'、'firstly'、'when'、'race'等)的取消操作
-
提供一种简单的方法来构建新的可取消的Promise类型
-
确保Promise分支正确取消。例如
import Alamofire
import PromiseKit
import CancelForPromiseKit
func updateWeather(forCity searchName: String) {
refreshButton.startAnimating()
let context = firstly {
getForecast(forCity: searchName)
}.done { response in
updateUI(forecast: response)
}.ensure {
refreshButton.stopAnimating()
}.catch { error in
// Cancellation errors are ignored by default
showAlert(error: error)
}.cancelContext
//…
// **** Cancels EVERYTHING (however the 'ensure' block always executes regardless)
context.cancel()
}
func getForecast(forCity name: String) -> CancellablePromise<WeatherInfo> {
return firstly {
Alamofire.request("https://autocomplete.weather.com/\(name)")
.responseDecodableCC(AutoCompleteCity.self)
}.then { city in
Alamofire.request("https://forecast.weather.com/\(city.name)")
.responseDecodableCC(WeatherResponse.self)
}.map { response in
format(response)
}
}
支持
如果您有任何问题或需要报告的问题,请使用我的错误追踪器。