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 {
        ...
    }

}