it-swarm-ja.com

ネストされたクロージャーは良いことですか?

私は今しばらくFirebaseでSwift開発を行っており、次のような多くのコードを作成していることがわかります。

Auth.auth().signIn(with: credential) { (user, error) in
    if let error = error {
        print("an error occurred while signing in!! \(error)")
        return
    }

    print("user is signed in with facebook!")

    FacebookHelper.getProfileInfo(completion: { (name, pictureUrl) in
        FirebaseHelper.setUser(user: user!, name: name, profileUrl: pictureUrl, completion: {
            self.performSegue(withIdentifier: "facebookLoggedInSegue", sender: nil)
        })
    })
}

これに取り組むより良い方法はありますか?そのようなものがobserve関数にネストされていると、混乱するだけです。

3
Forest Kunecke

ネストされた関数を持つ機能は良いことですが、紛らわしいコードは明らかに良いことではありません。この種のコードは、コードが例であると思われる非同期プログラミングのコンテキストではユビキタスです。

プロミスはそれを少し曖昧にすることができますが、問題を完全に解決するわけではありません。

これには2つの解決策があります。スレッドを使用するか、継続を使用します。

非同期APIを、スレッドとほぼすべての種類の同期構造を使用して、ブロッキングAPIに簡単に適合させることができます。これが AwaitKit の機能です。基本的には、非同期イベントをトリガーするだけで、完了コールバックでセマフォを通知し、元のスレッドでセマフォを待機します。 例:AwaitKit

ファーストクラス continuations もこの問題を解決するために使用できます。現在の継続をキャプチャし、それを非同期操作のコールバックとして渡し、現在の制御フローを破棄します。戻ることによって。少しだけラッピングすることで、このアプローチで非同期APIの外観をブロッキングAPIのようにすることができます。ただし、ファーストクラスの継続をサポートする言語はほとんどありません。それらをモデル化するために、継続渡しスタイル(またはもう少し制御するためにモナディックスタイル)を使用できます。実際、それがまさにこれらの「ネストされたコールバック」が行っていることです。

C#で一般化されたasync/await構文は、この問題を解決するために作成されました。概念的には(実際にはではありませんが)ローカルの継続渡しスタイル変換を実行し、そこから非同期APIをブロッキングAPIのように簡単に見せることができます。 (実際の変換ははるかに粗雑ですが、ネストされた関数を効率的に処理しないC#のコンテキストでは効率的です。)これはかなり良い解決策ですが、Swiftは現在そのようなものをサポートしていません構文。

このブログの投稿 私が完全に読んでいないものは、Swiftエコシステムの現在のオプションの一部と、いくつかの制限の説明です。C#では説明していません。 t async/awaitを理由なく言語とコンパイラに追加します。

あなたはこのようなものを試すことができます:

    func signIn(email: String, password: String) -> User? {
    var user: User? = nil
    DispatchQueue.global().async {
      let semaphore = DispatchSemaphore(value: 0)
      Auth.auth().signIn(withEmail: email, password: password) { (firUser, error) in
        if let firUser = firUser {
          user = User(id: firUser.uid, email: firUser.email, nickname: firUser.displayName)
        }
        semaphore.signal()
      }
      _ = semaphore.wait(timeout: .distantFuture)
    }
    return user
  }

次のように使用します。

if let user = self.authService.signIn(email: request.email, password: request.password) {
    print(user)
  }
1

それは実際にはかなり通常のコードです。わかりやすく、わかりやすい。あなたがそれに慣れると。次のように、関数の最後のパラメーターがクロージャーである場合は、簡略化された構文を利用できます。

FacebookHelper.getProfileInfo() { (name, pictureUrl) in
    FirebaseHelper.setUser(user: user!, name: name, profileUrl: pictureUrl) {
        self.performSegue(withIdentifier: "facebookLoggedInSegue", sender: nil)
    }
}
0
gnasher729