API 文档 | CancelForPromiseKit | CPKAlamofire | CPKCoreLocation | CPKFoundation |
---|
CancelForPromiseKit 为优秀的 PromiseKit 和 PromiseKit 扩展 提供了清晰简洁的取消功能。 虽然 PromiseKit 包含对取消的基本支持,但 CancelForPromiseKit 对此进行了扩展,使取消 promises 及其关联的任务变得简单明了。
例如
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 之间的差异以粗体突出显示。
在你的 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()
在上面的示例中:如果 fetch(avatar: creds.user)
返回一个标准的 Promise,则无法取消该 fetch。 但是,如果在 fetch 过程中调用 cancel,则一旦 fetch 完成,promise 链仍将被拒绝并抛出 PromiseCancelledError。 不会调用 done
代码块,而是调用 catch(policy: .allErrors)
代码块。
如果 fetch
返回一个 CancellablePromise,则在调用 cancel()
时,fetch 将被取消,并且会立即调用 catch
代码块。
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 提供核心的可取消 promises 和 Foundation 的扩展。 其他扩展可以通过在你的 Podfile
中指定额外的子规格来获得,例如
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
}
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 链被取消后,绝不 调用 promise 链中的后续代码块
完全支持并发,所有代码都是线程安全的
为所有适当的 PromiseKit 扩展(例如,Foundation、CoreLocation、Alamofire 等)提供可取消的变体
支持取消所有 PromiseKit 原语,例如 'after'、'firstly'、'when'、'race'
提供一种简单的方法来创建新型的可取消 promises
确保 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)
}
}
如果您有问题或要报告问题,请使用 我的 bug 跟踪器。