PinkyPromise 已成功退役。它的功能已由 Swift 语言和第一方库实现——这是长期支持的最佳情况。
如果您一直是 PinkyPromise 用户,请考虑您的迁移路径
Result
类型。Result
。一个 async
函数可以返回或抛出,所以现在有一种方法可以用简洁的语法和无需包装的类型来传递成功和失败的结果。async
函数提供了更安全的常规 Promise 功能。async
版本。如果没有,请使用 withCheckedContinuation
或 withCheckedThrowingContinuation
。它们需要相同的 complete-once 约定,但对该条件提供运行时检查。zip
和 zipArray
对 Promises 所做的那样。取消也可用。AsyncSequence
类型将 for
循环提升到异步世界以消费多结果任务,而此库仅提供单结果任务。Combine 框架的 AsyncPublisher
类型从类似 RxSwift 的可观察流桥接到 AsyncSequence
。感谢您使用 PinkyPromise!
一个微型的 Promises 库。
PinkyPromise 是 Swift 的 Promises 实现。它由两种类型组成
Result
- 一个值或错误。我们使用 Swift 内置的 Result TypePromise
- 一个在调用后某个时间产生 Result 的操作。Promises 可以被组合和排序。使用 PinkyPromise,您可以用安全、简洁、Swift 风格的代码运行异步操作的复杂组合。
github "willowtreeapps/PinkyPromise"
pod 'PinkyPromise'
Sources
文件夹中的文件复制到您的项目中。PinkyPromise
throw
/ catch
拥抱 Swift 语言PinkyPromise 旨在成为一个轻量级工具,可以完成大量繁重的工作。更精细的实现包括 Result 和 PromiseKit。
从下面的 示例 部分开始。
我们还编写了一个 playground 来演示 PinkyPromise 的优势和用法。请克隆存储库并在 Xcode 中打开 PinkyPromise.playground
。
超越 Results 和 Promises 的自然下一步是 Observable 类型。您可以使用 PinkyPromise 作为学习 RxSwift 的第一步,我们推荐这样做。
Promise 最适合运行可以成功或失败的异步操作。
Result 最适合表示此类操作的成功或失败。
iOS 上常见的异步操作模式是一个函数,它接受参数和一个完成块,然后开始工作。完成块将在工作完成时接收一个可选值和一个可选错误
func getString(withArgument argument: String, completion: ((String?, ErrorType?) -> Void)?) {
…
if successful {
completion?(value, nil)
} else {
completion?(nil, error)
}
}
getString(withArgument: "foo") { value, error in
if let value = value {
print(value)
} else {
print(error)
}
}
这是一个编译器不保证的松散契约。我们只是假设当 value
为 nil 时,error
不为 nil。
与标准的 Swift 可失败同步方法模式进行比较:像 Data(contentsOf:options:)
这样的函数将返回一个值或抛出一个错误,而不是两者都返回,也不是两者都不返回,也不是可选的。这是一个严密的契约。但是您不能在异步调用中使用该模式,因为您只能向后抛出您所在的函数,而不能向前抛出到完成块中。
以下是如何使用 Result 编写具有更严格契约的异步操作。Result 是成功或失败。它可以使用 return
或 throw
创建,并使用 value
检查,这将返回或抛出。
func getStringResult(withArgument argument: String, completion: ((Result<String, Error>) -> Void)?) {
…
completion?(Result {
if successful {
return value
} else {
throw error
}
})
}
getStringResult(withArgument: "foo") { result in
do {
print(try result.get())
} catch {
print(error)
}
}
在底层,Result<T, Error>
是一个具有两种情况的 enum
:.Success(T)
和 .Failure(Error)
。可以使用枚举 case 创建 Result,并使用 switch
检查它。但是由于 Result 表示返回的值或抛出的错误,我们更喜欢以上述风格使用它。
Promises 对于将多个异步操作组合成一个非常有用。为此,我们需要能够创建一个异步操作,而无需立即启动它。
要创建一个新的 Promise,您需要使用一个任务来创建它。任务本身是一个块,它接受一个完成块,通常称为 fulfill
。Promise 运行任务来完成其工作,当它完成时,任务将一个 Result
传递给 fulfill
。(提示:用于创建此 Promise 的任务与 getStringResult(withArgument:)
的主体相同。)
func getStringPromise(withArgument argument: String) -> Promise<String> {
return Promise { fulfill in
…
fulfill(Result {
if successful {
return value
} else {
throw error
}
})
}
}
let stringPromise = getStringPromise(withArgument: "bar")
stringPromise
捕获了它的任务,并且该任务捕获了参数。它是一个等待开始的操作。因此,使用 Promises,您可以创建操作,然后在稍后启动它们。您可以多次启动它们,或者根本不启动。
接下来,我们通过将完成块传递给 call
方法来要求 stringPromise
运行。call
运行任务并将 Result 路由回完成块。当 Promise 完成时,我们的完成块将接收到 Result,并且可以使用 try
和 catch
获取值或错误。
stringPromise.call { result in
do {
print(try result.get())
} catch {
print(error)
}
}
正如我们所见,使用 Promises,提供参数和提供完成块是独立的事件。Promise 的最大优势在于,在这两个事件之间,待完成的任务作为一个不可变的值存在。在函数式风格中,不可变值可以被转换和组合。
这是一个由多个 Promises 组成的复杂 Promise 的示例
let getFirstThreeChildrenOfObjectWithIDPromise =
getStringPromise(withArgument: "baz") // Promise<String>
.flatMap { objectID in
// String -> Promise<ModelObject>
Queries.getObjectPromise(withID: objectID)
}
.map { object in
// ModelObject -> [String]
let childObjectIDs = object.childObjectIDs
let count = max(3, childObjectIDs.count)
return childObjectIDs[0..<count]
}
.flatMap { childObjectIDs in
// [String] -> Promise<[ModelObject]>
zipArray(childObjectIDs.map { childObjectID
// String -> Promise<ModelObject>
Queries.getObjectPromise(withID: childObjectID)
})
}
getFirstThreeChildrenOfObjectWithIDPromise
是一个单一的异步操作,它由许多小的操作组成。它
即使此操作有许多步骤依赖于先前操作的成功,我们也不必通过编写多个完成块来协调它们。相反,我们只需处理最终结果,使用 Result 提供的严格契约
getFirstThreeChildrenOfObjectWithIDPromise.call { [weak self] result in
do {
self?.updateViews(withObjects: try result.get())
} catch {
self?.showError(error)
}
}
我们打算保持 PinkyPromise 完全单元测试。
您可以在 Xcode 中运行测试,或者使用带有 Fastlane 的 bundle exec fastlane run_tests
。
我们在 CircleCI 上运行持续集成。
欢迎贡献。请参阅 贡献指南。
PinkyPromise 采用了由 Contributor Covenant 定义的 行为准则,这与 Swift 语言 和无数其他开源软件团队使用的相同。