- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我对 EventLoop futures 和 Promise 很陌生。我的软件堆栈:
我有一些工作要做,并正在寻找有关如何改进它的建议,因为我对 .map
、.flatMap
、.always
等
以下是 iOS 应用程序中我的 gRPC 数据单例的相关函数:
import Foundation
import NIO
import GRPC
class DataRepository {
static let shared = DataRepository()
// skip ...
func readItem(id: Int64, eventLoop: EventLoop) -> EventLoopFuture<V1_ReadResponse> {
// TODO: Is this the right place?
defer {
try? eventLoop.syncShutdownGracefully()
}
let promise = eventLoop.makePromise(of: V1_ReadResponse.self)
var request = V1_ReadRequest()
request.api = "v1"
request.id = id
let call = client.read(request, callOptions: callOptions) // client - GRPCClient initialized in the singleton
call.response.whenSuccess{ response in
return promise.succeed(response)
}
call.response.whenFailure{ error in
return(promise.fail(error))
}
return promise.futureResult
}
我在 SwiftUI View 中的代码:
import SwiftUI
import NIO
struct MyView : View {
@State private var itemTitle = "None"
var body: some View {
Text(itemTitle)
}
func getItem() {
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let result = DataRepository.shared.readItem(id: 1, eventLoop: eventLoopGroup.next())
_ = result.always { (response: Result<V1_ReadResponse, Error>) in
let res = try? response.get()
if let resExist = res {
self.itemTitle = resExist.item.title
}
_ = response.mapError{ (err: Error) -> Error in
print("[Error] Connection error or item not found: \(err)")
return err
}
}
}
我应该重构 getItem
和/或 readItem
吗?有什么具体建议吗?
最佳答案
我有一个非常具体的建议,然后是一些一般性的建议。第一个也是最具体的建议是,如果您不编写 NIO 代码,则可能不需要创建 EventLoop
根本没有。 grpc-swift 将创建自己的事件循环,您可以简单地使用它们进行回调管理。
这可以让您重构您的 readItem
代码简单地是:
func readItem(id: Int64, eventLoop: EventLoop) -> EventLoopFuture<V1_ReadResponse> {
var request = V1_ReadRequest()
request.api = "v1"
request.id = id
let call = client.read(request, callOptions: callOptions) // client - GRPCClient initialized in the singleton
return call.response
}
这极大地简化了您的代码,让您可以将管理事件循环的所有复杂工作基本上推迟到 grpc-swift,这样您就可以担心您的应用程序代码了。
否则,这里有一些一般性建议:
位于 readItem
的顶部你有这个代码:
// TODO: Is this the right place?
defer {
try? eventLoop.syncShutdownGracefully()
}
您可以看到我在上面的示例中删除了它。那是因为这不是合适的地方。事件循环是长期存在的对象:它们的构建和关闭成本很高,因此您很少需要这样做。一般来说,您希望事件循环由相当高级的对象(如 AppDelegate
或某些高级 View Controller 或 View
)构建和拥有。然后它们将被系统中的其他组件“借用”。对于您的特定应用程序,正如我所指出的,您可能希望事件循环由 grpc-swift 拥有,因此您不应该关闭任何事件循环,但一般来说,如果您采取此策略,则适用一个明确的规则:如果您没有创建 EventLoop
,不要关闭它。它不是你的,你只是借用它。
事实上,在 NIO 2.5.0 中,NIO 团队made it an error以这种方式关闭事件循环:如果您替换 try?
与 try!
您会看到应用程序崩溃。
在您的 MyView.getItem
中函数您创建一个 MultiThreadedEventLoopGroup
。我上面的建议是,您根本不要创建自己的事件循环,但如果您要这样做,这不是一个好地方。
对于 SwiftUI,最好的办法就是让你的 EventLoopGroup
成为 EnvironmentObject
由 AppDelegate
注入(inject)到 View 层次结构中。每个需要 EventLoopGroup
的 View 然后可以安排从环境中提取一个事件循环,这允许您在应用程序中的所有 View 之间共享事件循环。
EventLoopGroup
使用自己的线程的私有(private)池来执行回调和应用程序工作。在 getItem
您可以从这些 future 回调之一修改 View 状态,而不是从主队列。
您应该使用DispatchQueue.main.async { }
在修改状态之前重新加入主队列。您可能想将其包装在一个辅助函数中,如下所示:
extension EventLoopFuture {
func always<T>(queue: DispatchQueue, _ body: (Result<T>) -> Void) {
return self.always { result in
queue.async { body(result) }
}
}
}
作为生活质量的一个小问题,这段代码可以重构:
let res = try? response.get()
if let resExist = res {
self.itemTitle = resExist.item.title
}
_ = response.mapError{ (err: Error) -> Error in
print("[Error] Connection error or item not found: \(err)")
return err
}
对此:
switch response {
case .success(let response):
self.itemTitle = response.item.title
case .failure(let err):
print("[Error] Connection error or item not found: \(err)")
}
关于swift - 如何在Swift中正确使用EventLoopFuture?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59119038/
有没有办法在 .swift 文件(编译成 .swift 模块)中声明函数,如下所示: 你好.swift func hello_world() { println("hello world")
我正在尝试使用 xmpp_messenger_ios 和 XMPPFramework 在 iOS 上执行 MUC 这是加入房间的代码。 func createOrJoinRoomOnXMPP()
我想在我的应用程序上创建一个 3D Touch 快捷方式,我已经完成了有关快捷方式本身的所有操作,它显示正确,带有文本和图标。 当我运行这个快捷方式时,我的应用程序崩溃了,因为 AppDelegate
我的代码如下: let assetTag = Expression("asset_tag") let query2 = mdm.select(mdm[assetTag],os, mac, lastRe
我的 swift 代码如下所示 Family.arrayTuple:[(String,String)]? = [] Family.arrayTupleStorage:String? Family.ar
这是我的 JSON,当我读取 ord 和 uniq 数据时出现错误 let response2 : [String: Any] = ["Response":["status":"SUCCESS","
我想将 swift 扩展文件移动到 swift 包中。但是,将文件移动到 swift 包后,我遇到了这种错误: "Type 'NSAttributedString' has no member 'ma
使用CocoaPods,我们可以设置以下配置: pod 'SourceModel', :configurations => ['Debug'] 有什么方法可以用 Swift Package Manag
我正在 Xcode 中开发一个 swift 项目。我将其称为主要项目。我大部分都在工作。我在日期选择器、日期范围和日期数学方面遇到了麻烦,因此我开始了另一个名为 StarEndDate 的项目,其中只
这是 ObjectiveC 代码: CCSprite *progress = [CCSprite spriteWithImageNamed:@"progress.png"]; mProgressBar
我正在创建一个命令行工具,在 Xcode 中使用 Swift。我想使用一个类似于 grunt 的配置文件确实如此,但我希望它是像 Swift 包管理器的 package.swift 文件那样的快速代码
我假设这意味着使用系统上安装的任何 swift 运行脚本:#!/usr/bin/swift 如何指定脚本适用的解释器版本? 最佳答案 Cato可用于此: #!/usr/bin/env cato 1.2
代码说完全没问题,没有错误,但是当我去运行模拟器的时候,会出现这样的字样: (Swift.LazyMapCollection (_base:[ ] 我正在尝试创建一个显示报价的报价应用。 这是导入
是否可以在运行 Swift(例如 Perfect、Vapor、Kitura 等)的服务器上使用 RealmSwift 并使用它来存储数据? (我正在考虑尝试将其作为另一种解决方案的替代方案,例如 no
我刚开始学习编程,正在尝试完成 Swift 编程书中的实验。 它要求““编写一个函数,通过比较两个 Rank 值的原始值来比较它们。” enum Rank: Int { case Ace = 1 ca
在您将此问题标记为重复之前,我检查了 this question 它对我不起作用。 如何修复这个错误: error: SWIFT_VERSION '5.0' is unsupported, suppo
从 Xcode 9.3 开始,我在我的模型中使用“Swift.ImplicitlyUnwrappedOptional.some”包裹了我的字符串变量 我不知道这是怎么发生的,但它毁了我的应用程序! 我
这个问题在这里已经有了答案: How to include .swift file from other .swift file in an immediate mode? (2 个答案) 关闭 6
我正在使用 Swift Package Manager 创建一个应用程序,我需要知道构建项目的配置,即 Debug 或 Release。我试图避免使用 .xcodeproj 文件。请有人让我知道这是否
有一个带有函数定义的文件bar.swift: func bar() { println("bar") } 以及一个以立即模式运行的脚本foo.swift: #!/usr/bin/xcrun s
我是一名优秀的程序员,十分优秀!