fetchUserId().then { id in
print("UserID : \(id)")
}.onError { e in
print("An error occured : \(e)")
}.finally {
print("Everything is Done :)")
}
let userId = try! awaitPromise(fetchUserId())
因为异步代码很难编写,很难阅读,很难理解。 维护起来很痛苦
then 是 freshOS iOS 工具集的一部分。 在示例 App 中尝试一下! 下载启动项目
通过使用 then 关键字,使您可以编写读起来像英语句子的异步代码
异步代码现在变得简洁、灵活且易于维护 ❤️
Promise
/ Future
概念Async
/ Await
progress
race
recover
validate
retry
bridgeError
chain
noMatterWhat
...fetchUserId({ id in
fetchUserNameFromId(id, success: { name in
fetchUserFollowStatusFromName(name, success: { isFollowed in
// The three calls in a row succeeded YAY!
reloadList()
}, failure: { error in
// Fetching user ID failed
reloadList()
})
}, failure: { error in
// Fetching user name failed
reloadList()
})
}) { error in
// Fetching user follow status failed
reloadList()
}
🙉🙈🙊#callbackHell
fetchUserId()
.then(fetchUserNameFromId)
.then(fetchUserFollowStatusFromName)
.then(updateFollowStatus)
.onError(showErrorPopup)
.finally(reloadList)
fetchUserId().then { id in
print("UserID : \(id)")
}.onError { e in
print("An error occured : \(e)")
}.finally {
print("Everything is Done :)")
}
如果我们要使其易于维护,它应该读起来像英语句子
我们可以通过将我们的代码块提取到单独的函数中来实现这一点
fetchUserId()
.then(printUserID)
.onError(showErrorPopup)
.finally(reloadList)
现在,它变得简洁、灵活、易于维护,并且读起来像英语句子 <3
精神健全得以保存 // #告别回调地狱
想知道 fetchUserId() 是什么吗?
它是一个简单的函数,返回一个强类型的 promise
func fetchUserId() -> Promise<Int> {
return Promise { resolve, reject in
print("fetching user Id ...")
wait { resolve(1234) }
}
}
在这里,您通常会将虚拟等待函数替换为您的网络请求 <3
至于 then
和 onError
,您还可以调用 progress
块来处理诸如上传头像之类的事情。
uploadAvatar().progress { p in
// Here update progressView for example
}
.then(doSomething)
.onError(showErrorPopup)
.finally(doSomething)
我们的实现与原始 javascript Promises 略有不同。 事实上,它们并非立即启动,这是故意的。 调用 then
、onError
或 finally
将自动启动它们。
调用 then
会启动一个 promise,如果它尚未启动。 在某些情况下,我们只想注册一些代码以供稍后使用。 例如,在 JSON 到 Swift 模型解析的情况下,我们经常希望将解析块附加到 JSON promises,但无需启动它们。
为了做到这一点,我们需要使用 registerThen
代替。 它与 then
完全相同,只是不立即启动 promise。
let fetchUsers:Promise<[User]> = fetchUsersJSON().registerThen(parseUsersJSON)
// Here promise is not launched yet \o/
// later...
fetchUsers.then { users in
// YAY
}
请注意,onError
和 finally
也具有其非启动的对应项:registerOnError
和 registerFinally
。
通常我们需要返回一个拒绝的 promise 如下
return Promise { _, reject in
reject(anError)
}
可以用以下快捷方式编写
return Promise.reject(error:anError)
使用 race
,您可以发送多个任务并获得第一个返回的结果
race(task1, task2, task3).then { work in
// The first result !
}
使用 .recover
,你可以为失败的 Promise 提供一个备用值。
您可以
.recover(with: 12)
.recover(MyError.defaultError, with: 12)
.recover { e in
if e == x { return 32 }
if e == y { return 143 }
throw MyError.defaultError
}
.recover { e -> Promise<Int> in
// Deal with the error then
return Promise<Int>.resolve(56)
// Or
return Promise<Int>.reject(e)
}
}
.recover(with: Promise<Int>.resolve(56))
请注意,在块版本中,您还可以抛出自己的错误 \o/
使用 .validate
,您可以使用断言块来打破 promise 链。
您可以
例如,检查用户是否允许饮酒
fetchUserAge()
.validate { $0 > 18 }
.then { age in
// Offer a drink
}
.validate(withError: MyError.defaultError, { $0 > 18 })`
默认情况下,失败的验证将返回 PromiseError.validationFailed
。
使用 retry
,您可以重新启动失败的 Promise X 次。
doSomething()
.retry(10)
.then { v in
// YAY!
}.onError { e in
// Failed 10 times in a row
}
使用 .bridgeError
,你可以拦截一个低级错误并返回你自己的高级错误。 典型的用例是当你收到一个 api 错误,然后你把它桥接到你自己的领域错误。
您可以
.bridgeError(to: MyError.defaultError)
.bridgeError(SomeError, to: MyError.defaultError)
使用 .whenAll
,您可以组合多个调用,并在所有 promise 都实现时获取所有结果
whenAll(fetchUsersA(),fetchUsersB(), fetchUsersC()).then { allUsers in
// All the promises came back
}
使用 chain
,您可以添加行为,而无需更改 Promise 链。
一个常见的用例是添加像这样的分析跟踪
extension Photo {
public func post() -> Async<Photo> {
return api.post(self).chain { _ in
Tracker.trackEvent(.postPicture)
}
}
}
使用 noMatterWhat
,您可以添加代码以在 promise 链的中间执行,无论发生什么情况。
func fetchNext() -> Promise<[T]> {
isLoading = true
call.params["page"] = page + 1
return call.fetch()
.registerThen(parseResponse)
.resolveOnMainThread()
.noMatterWhat {
self.isLoading = false
}
}
使用 unwrap
,您可以将 optional 转换为 promise
func fetch(userId: String?) -> Promise<Void> {
return unwrap(userId).then {
network.get("/user/\($0)")
}
}
如果值为 nil,Unwrap 将使用 unwrappingFailed
错误使 promise 链失败 :)
对于我们这些认为 Async 比 Promise
更清晰的人,提供了 AsyncTask
和 Async<T>
类型别名。 可以根据需要将 Promise<Void>
替换为 AsyncTask
,将 Promise<T>
替换为 Async<T>
。
这纯粹是为了视觉效果 :)
awaitPromise
同步等待 promise 完成并产生结果
let photos = try! awaitPromise(getPhotos())
async
接受一个块并将其包装在一个后台 Promise 中。
async {
let photos = try awaitPromise(getPhotos())
}
请注意,我们不再需要 !
,因为 async
将捕获错误。
总之,async
/awaitPromise
使我们能够以同步方式编写异步代码
async {
let userId = try awaitPromise(fetchUserId())
let userName = try awaitPromise(fetchUserNameFromId(userId))
let isFollowed = try awaitPromise(fetchUserFollowStatusFromName(userName))
return isFollowed
}.then { isFollowed in
print(isFollowed)
}.onError { e in
// handle errors
}
Await 带有 ..
简写运算符。 ..?
将回退到 nil 值而不是抛出。
let userId = try awaitPromise(fetchUserId())
可以这样写
let userId = try ..fetchUserId()
Swift Package Manager (SPM) 现在是安装 Then
的官方方式。 从 5.1.3
开始,其他包管理器已被弃用,并且未来的版本将不再支持。
Xcode
> 文件
> Swift Packages
> Add Package Dependency...
> 粘贴
https://github.com/freshOS/Then
target 'MyApp'
pod 'thenPromise'
use_frameworks!
github "freshOS/then"
S4cha, Max Konovalov, YannickDot, Damien, piterlouis
喜欢这个项目吗? 请我们喝咖啡或每月捐款支持我们,并帮助我们继续开展活动 :)
成为赞助商,让您的徽标显示在我们在 Github 上的 README 文件中,并链接到您的网站 :)