测试已测试 | ✓ |
Lang语言 | SwiftSwift |
许可证 | MIT |
发布最后发布 | 2016年7月 |
SPM支持 SPM | ✗ |
Maintained by Cristian Kocza.
Swift 实现Promises/A+提议的尝试,完全支持泛型,以便能够利用 Swift 强大的类型系统。完整的规范可以在 http://promisesaplus.com/ 找到。
实现尝试尽可能地遵循 Promise/A+ 规范,但是受 Swift 强类型系统的限制,并非全部都能遵循。
当前实现尚未实现循环承诺链检测,将在以后添加对此的支持。
git clone https://github.com/cristik/CKPromise.Swift.git
CKPromise.Swift.xcproject
添加到您的项目/工作区CKPromise_Swift
目标让我们看看承诺的实际效果。我们从一个简单的任务开始 - 发送一个 NSURLSession
请求,并将接收到的数据解析到字典中。
首先,让我们扩展 NSURLSession
和 NSData
以为其提供发送请求和解析 JSON 的承诺支持
extension NSURLSession {
func sendRequest(request: NSURLRequest) -> Promise<NSData,NSError> {
let promise = Promise<NSData,NSError>()
let task = self.dataTaskWithRequest(request) { data, urlResponse, error in
if let error = error {
// we have an error, means the request failed, reject promise
promise.reject(error)
} else if let data = data {
// we don't have an error and we have data, resolve promise
promise.resolve(data)
} else {
// we have neither error, nor data, report a generic error
// another approach would have been to resolve the promise
// with an empty NSData object
promise.reject(NSError.genericError())
}
}
task.resume()
return promise
}
}
extension NSData {
func parseJSON() -> Promise<[NSObject:AnyObject], NSError> {
let promise = Promise<[NSObject:AnyObject], NSError>()
if let parsedJSON = try? NSJSONSerialization.JSONObjectWithData(self, options: []),
let result = parsedJSON as? [NSObject:AnyObject] {
// yay, we were able to parse, and received a dictionary
promise.resolve(result)
} else {
// :( report an invalid json error
promise.reject(NSError.invalidJSONError())
}
return promise
}
}
我们可以像以下这样使用上面的扩展:
let request = NSURLRequest(URL: NSURL(string: "https://jsonplaceholder.typicode.com/posts/1")!)
NSURLSession.sharedSession().sendRequest(request).onSuccess({
return $0.parseJSON()
}).onSuccess( {
print("Parsed JSON: \($0)")
}).onFailure( {
print("Failed with error: \($0)")
})
sendRequest
委托成功地回调返回另一个承诺,即 JSON 解析承诺,这使我们能够很好地连接承诺。如果两个承诺中的任何一个失败,执行将直接转到最后的失败处理程序,这有助于我们,因为我们不需要编写多个失败处理程序。
这看起来似乎不多,所以让我们再增加一步:从解析的字典中创建一个 Post
实体。以下是关于承诺方面的 Post
的可能实现:
struct Post {
private(set) var id: Int = 0
private(set) var userId: Int = 0
private(set) var title: String = ""
private(set) var body: String = ""
static func fromDictionary(dictionary: [NSObject:AnyObject]) -> Promise<Post,NSError> {
let promise = Promise<Post,NSError>()
guard let id = dictionary["id"] as? Int,
userId = dictionary["userId"] as? Int else {
promise.reject(NSError.invalidDictionaryError())
return promise
}
var post = Post()
post.id = id
post.userId = userId
post.title = dictionary["title"] as? String ?? ""
post.body = dictionary["body"] as? String ?? ""
promise.resolve(post)
return promise
}
}
基本上,我们添加了对从字典创建 Post
的支持,采用一种承诺风格的方式。我们如何使用它?很简单:
let request = NSURLRequest(URL: NSURL(string: "https://jsonplaceholder.typicode.com/posts/1")!)
NSURLSession.sharedSession().sendRequest(request).onSuccess({
return $0.parseJSON()
}).onSuccess({
return Post.fromDictionary($0)
}).onSuccess({
print("Parsed post: \($0)")
}).onFailure( {
print("Failed with error: \($0)")
})
我们可以进一步扩展承诺,直到我们所需。
现在,让我们回到 NSURLSession
。还记得 sendRequest
方法吗?如果我们还想返回数据及其 URL 响应,该怎么办?这并不难,多亏了元组。
extension NSURLSession {
func sendRequest(request: NSURLRequest) -> Promise<(NSURLResponse, NSData),NSError> {
let promise = Promise<(NSURLResponse, NSData),NSError>()
let task = self.dataTaskWithRequest(request) { data, urlResponse, error in
if let error = error {
promise.reject(error)
} else if let data = data, urlResponse = urlResponse {
promise.resolve((urlResponse, data))
} else {
promise.reject(NSError.genericError())
}
}
task.resume()
return promise
}
}
现在,大多数时候我们会发送 HTTP 请求,如果我们能够无需在回调用中降级到强制转换就能使用 NSHTTPURLResponse
子类,那将是件好事。类似如下所示:
func sendHTTPRequest(request: NSURLRequest) -> Promise<(NSHTTPURLResponse, NSData),NSError>
做这件事并不难,但首先我们需要对 sendRequest
方法稍作调整
extension NSURLSession {
func sendRequest<T: NSURLResponse)(request: NSURLRequest) -> Promise<(T, NSData),NSError> {
let promise = Promise<(T, NSData),NSError>()
let task = self.dataTaskWithRequest(request) { data, urlResponse, error in
if let error = error {
promise.reject(error)
} else if let data = data, urlResponse = urlResponse as? T {
promise.resolve((urlResponse, data))
} else {
promise.reject(NSError.genericError())
}
}
task.resume()
return promise
}
func sendHTTPRequest(request: NSURLRequest) -> Promise<(NSHTTPURLResponse, NSData),NSError> {
return sendRequest(request)
}
}
这就像那样简单,多亏了泛型的支持。
但是等等,如果URL请求不对应于HTTP请求怎么办?在这种情况下,我们可能想要快速失败,甚至不发送请求,而不是在收到服务器响应后在进行降阶步骤时失败。嗯,正如你所猜测的,这也不难。
func sendHTTPRequest(request: NSURLRequest) -> Promise<(NSHTTPURLResponse, NSData),NSError> {
guard ["http", "https"].contains(request.url.scheme) else {
return Promise.rejected(NSError.invalidRequestError())
}
return sendRequest(request)
}
承诺的另一个常见情况是恢复失败。一个虚构的例子是在资源失败时,使用PUT尝试恢复失败的POST操作。这就是如何实现这种情况。
let postRequest = NSURLRequest(...)
let putRequest = NSURLRequest(...)
NSURLSession.sharedSession().sendRequest(postRequest).onFailure({
// if the post request fails, try with a put one
return NSURLSession.sharedSession().sendRequest(putRequest)
}).onSuccess({
// we end up here in two cases: either the post request succeeded, or it failed
// and the put one succeeded
}).onFailure({
// both requests failed
})
再次,承诺允许我们以线性流程(我说得更自然一点)声明数据处理。