Le chiusure annidate sono una buona cosa?

1

Da un po 'di tempo faccio lo sviluppo di Swift con Firebase, e mi trovo a creare un sacco di codice come questo:

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)
        })
    })
}

C'è un modo migliore per avvicinarsi a questo? Può solo creare confusione quando ho cose come quelle annidate in observe funzioni.

    
posta Forest Kunecke 15.08.2017 - 02:28
fonte

3 risposte

1

La capacità di avere ha funzioni annidate è una buona cosa, ma ovviamente qualsiasi codice che sia confuso non è una buona cosa. Questo tipo di codice è onnipresente nel contesto di una programmazione asincrona di cui il tuo codice sembra essere un esempio.

Le promesse possono renderlo un po 'meno fugace, ma non risolvono completamente il problema.

Ci sono due soluzioni a questo. Utilizza i thread o utilizza le continuazioni.

È facile adattare un'API asincrona a una bloccante utilizzando thread e praticamente qualsiasi tipo di costrutto di sincronizzazione. Questo è ciò che fa AwaitKit . Fondamentalmente, si attiva solo un evento asincrono e nel richiamo di completamento si segnala un semaforo, mentre si attende il semaforo nel thread originale. E.g. in AwaitKit .

Anche le continuazioni di prima classe possono essere utilizzate per risolvere questo problema. Si cattura la continuazione di corrente e la si passa come richiamo all'operazione asincrona, quindi si abbandona il flusso di controllo corrente, ad es. tornando. Con solo un po 'di wrapping, puoi rendere un'API asincrona simile a un'API di blocco con questo approccio. Poche lingue supportano continuazioni di prima classe, però. Per modellarli, puoi usare lo stile di passaggio di continuazione (o lo stile monadico per un controllo un po 'maggiore), e in effetti, questo è esattamente ciò che stanno facendo questi "callback annidati".

La sintassi async / await resa popolare da C # è stata creata per risolvere questo problema. Concettualmente (ma non in realtà) esegue una trasformazione di stile di passaggio di continuazione locale, e da lì è facile rendere un'API asincrona simile a una di blocco. (La trasformazione effettiva è molto più grossa ma efficiente nel contesto di C # che non gestisce le funzioni annidate in modo efficiente.) Questa è una soluzione abbastanza buona, ma Swift attualmente non supporta tale sintassi.

Questo post del blog che non ho letto completamente sembra dettagliare alcune delle opzioni correnti nel Ecosistema rapido oltre a spiegare alcuni dei limiti. C # non ha aggiunto async / await alla lingua e al compilatore senza motivo.

    
risposta data 15.08.2017 - 03:44
fonte
0

In realtà è un codice abbastanza normale. Semplice, facile da capire. Una volta che ti ci abitui. Puoi sfruttare la sintassi semplificata se l'ultimo parametro di una funzione è una chiusura, come questa:

FacebookHelper.getProfileInfo() { (name, pictureUrl) in
    FirebaseHelper.setUser(user: user!, name: name, profileUrl: pictureUrl) {
        self.performSegue(withIdentifier: "facebookLoggedInSegue", sender: nil)
    }
}
    
risposta data 15.08.2017 - 20:23
fonte
0

Puoi provare qualcosa del genere:

    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
  }

e usalo in questo modo:

if let user = self.authService.signIn(email: request.email, password: request.password) {
    print(user)
  }
    
risposta data 24.09.2017 - 18:41
fonte

Leggi altre domande sui tag