Completion Handler 是否结束函数?
Does a Completion Handler end the function?
也许我不明白完成处理程序的概念,但我有这样的功能;
func someFunction(completion: @escaping (Bool, LoginError?) -> Void) {
self.checkDevice() { allowed, error in
if let e = error {
completion(false, e)
}
completion(true, nil)
}
}
虽然了解 checkDevice()
所做的事情,但基本前提是它执行异步网络调用,并且 returns true 没有错误(nil),或 returns false 错误。
当我 运行 这段代码时,我发现完成处理程序被调用了两次。它以元组(false, error
)和(true, nil
)的形式发送完成。我已经进行了一些调试,但似乎无法调用 someFunction()
两次。
我相信一旦发送 completion,函数就会结束。在我的测试用例中,我强制从 checkDevice()
中出错,这应该导致我将完成作为 (false, error
) 发送,但我同时看到 (false, error
) 和 (true, nil
).完成不是立即结束函数吗?
完成处理程序不会结束函数。结束一个函数的典型方式是插入一个return
。
查看评论回复:您的具体案例
I was of the belief that once a completion is sent, the function would end.
不,为什么会这样?当您调用此函数时,就像调用任何其他函数一样。 名字没有魔力。
改写成这样:
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,它应该采用 一个 参数——一个结果,它包含我们是否成功或失败,如果失败,错误是什么:
func someFunction(completion: @escaping (Result<Void, Error>) -> Void) {
self.checkDevice() { allowed, error in
completion( Result {
if let e = error { throw e }
})
}
}
如您所见,使用结果作为参数可以让您更优雅地响应发生的事情。
事实上,checkDevice
本身可以将结果传递给 它的 完成处理程序,然后事情会更加优雅(和简单)。
考虑包括参数名称(供参考)。完成应该只调用一次。您可以使用 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 {
...
}
}
也许我不明白完成处理程序的概念,但我有这样的功能;
func someFunction(completion: @escaping (Bool, LoginError?) -> Void) {
self.checkDevice() { allowed, error in
if let e = error {
completion(false, e)
}
completion(true, nil)
}
}
虽然了解 checkDevice()
所做的事情,但基本前提是它执行异步网络调用,并且 returns true 没有错误(nil),或 returns false 错误。
当我 运行 这段代码时,我发现完成处理程序被调用了两次。它以元组(false, error
)和(true, nil
)的形式发送完成。我已经进行了一些调试,但似乎无法调用 someFunction()
两次。
我相信一旦发送 completion,函数就会结束。在我的测试用例中,我强制从 checkDevice()
中出错,这应该导致我将完成作为 (false, error
) 发送,但我同时看到 (false, error
) 和 (true, nil
).完成不是立即结束函数吗?
完成处理程序不会结束函数。结束一个函数的典型方式是插入一个return
。
查看评论回复:您的具体案例
I was of the belief that once a completion is sent, the function would end.
不,为什么会这样?当您调用此函数时,就像调用任何其他函数一样。 名字没有魔力。
改写成这样:
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,它应该采用 一个 参数——一个结果,它包含我们是否成功或失败,如果失败,错误是什么:
func someFunction(completion: @escaping (Result<Void, Error>) -> Void) {
self.checkDevice() { allowed, error in
completion( Result {
if let e = error { throw e }
})
}
}
如您所见,使用结果作为参数可以让您更优雅地响应发生的事情。
事实上,checkDevice
本身可以将结果传递给 它的 完成处理程序,然后事情会更加优雅(和简单)。
考虑包括参数名称(供参考)。完成应该只调用一次。您可以使用 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 {
...
}
}