完成处理程序是不是结束功能?
Posted
技术标签:
【中文标题】完成处理程序是不是结束功能?【英文标题】:Does a Completion Handler end the function?完成处理程序是否结束功能? 【发布时间】:2020-01-30 22:49:50 【问题描述】:也许我不明白完成处理程序的概念,但我有一个这样的函数;
func someFunction(completion: @escaping (Bool, LoginError?) -> Void)
self.checkDevice() allowed, error in
if let e = error
completion(false, e)
completion(true, nil)
虽然对checkDevice()
所做的事情很清楚,但基本前提是它执行异步网络调用,并返回 true 且没有错误 (nil),或返回 false 出现错误。
当我运行这段代码时,我发现完成处理程序被调用了两次。它以元组(false, error
)和(true, nil
)的形式发送完成。我已经进行了一些调试,似乎没有任何方式可以调用两次 someFunction()
。
我相信一旦发送了completion,函数就会结束。在我的测试用例中,我强制来自checkDevice()
的错误,这将导致我将完成发送为(false, error
),但我同时看到(false, error
)和(true, nil
)。完成不会立即结束函数吗?
【问题讨论】:
不,它没有。完成处理程序只是一个您可以调用的闭包,被命名为“完成”并没有赋予它任何特殊属性 只需将第二个completion(true, nil)
调用放在if
的else
子句中即可。
【参考方案1】:
我相信一旦发送完成,函数就会结束。
不,为什么会这样?当你调用这个函数时,它就像调用任何其他函数一样。 名字没有魔力。
这样改写:
func someFunction(completion: @escaping (Bool, LoginError?) -> Void)
self.checkDevice() allowed, error in
if let e = error
completion(false, e)
return // *
completion(true, nil)
实际上,我应该提一下,这是编写完成处理程序的一种糟糕方式。与其采用两个参数,一个 Bool 和一个仅当 Bool 为 false
时才使用的可选 LoginError,它应该采用 one 参数 — 一个 Result,它携带我们成功或失败的两个参数,并且,如果我们失败了,错误是什么:
func someFunction(completion: @escaping (Result<Void, Error>) -> Void)
self.checkDevice() allowed, error in
completion( Result
if let e = error throw e
)
如您所见,使用 Result 作为参数可以让您更优雅地响应所发生的事情。
确实,checkDevice
本身可以将 Result 传递给 其 完成处理程序,然后事情会更加优雅(和简单)。
【讨论】:
感谢您的回复。我很欣赏细节。在基于团队的环境中是否有遵循这种心态的最佳实践? IE。对于任何新的开发人员来说,Result 现在是另一个说开发人员必须遵循和理解的类。与 Bool 元组相比,LoginError(可能被认为是错误)对于不熟悉代码的人来说会更清楚,不是吗? 在我看来,结果更加清晰。我们有 one 参数,而不是必须猜测其含义的两个参数,一个枚举,其 case 为.success
和 .failure
(以及一个有效负载,例如失败的情况)。还有什么比这更清楚的呢?从结果中提取该信息非常优雅和简单,比测试 Optional 的 nil 或 Bool 的 true 好得多。
谢谢,@matt。我已经开始与一个团队合作,并试图了解“最佳实践”。我觉得我创建的自定义类越多,我为团队创建的工作就越多,因为他们必须深入研究这些类的含义。但是我正在处理的项目有许多自定义类,所以我理解你提供的观点。谢谢!
我已经审查并实施了您的建议,并且确实看到了清晰度上的差异。感谢您分享这个。这是一个重要的学习课程和帮助!【参考方案2】:
完成处理程序不会结束函数。结束函数的典型方法是插入return
。
查看评论:您的具体情况
【讨论】:
在您的情况下,您的self.checkDevice()
可能会多次返回,以便您的完成块被多次命中。我的怀疑是你可能在 self.checkDevice
函数中有一些东西,假设完成处理程序结束了函数。
这就回答了这个问题!非常感谢!【参考方案3】:
考虑包括参数名称(供参考)。并且完成应该只被调用一次。您可以使用return
或使用完整的if-else
条件来做到这一点。
func someFunction(completion: @escaping (_ done: Bool, _ error: LoginError?) -> Void)
checkDevice() (allowed, error) in
if let e = error
completion(false, e)
else
completion(true, nil)
func someFunction(completion: @escaping (_ done: Bool, _ error: LoginError?) -> Void)
checkDevice() (allowed, error) in
if let e = error
completion(false, e)
return
completion(true, nil)
通过给参数命名,调用函数时,现在可以引用签名来表示其参数的含义:
someFunction (done, error) in
if let error = error
...
else if done
...
【讨论】:
谢谢,@bsod。我很欣赏这方面的细节!以上是关于完成处理程序是不是结束功能?的主要内容,如果未能解决你的问题,请参考以下文章