2017-04-10 1 views
3

Ich Ausführung einige Funktionen in einem Test asynchron eine Absende-Warteschlange wie folgt aus:Gibt es eine Möglichkeit, Fehler von asynchronen Schließungen in Swift 3 zu werfen?

let queue: DispatchQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated) 
let group: DispatchGroup = DispatchGroup() 

func execute(argument: someArg) throws { 
    group.enter() 
    queue.async { 
    do { 
     // Do stuff here 
     group.leave() 
    } catch { 
     Log.info(“Something went wrong") 
    } 
    } 
    group.wait() 
} 

der Code innerhalb der „do“ Block kann manchmal Fehler werfen, dass ich später zu fangen. Da ich einen Test entwickle, möchte ich, dass der Test fehlschlägt, wenn der Code innerhalb des "do" -Blocks einen Fehler auslöst. Gibt es eine Möglichkeit, einen Fehler zu verursachen, ohne ihn innerhalb des "queue.async" -Aufrufs abzufangen?

Antwort

0

Refactor Ihren Code zu verwenden queue.sync und dann werfen Sie Ihren Fehler von dort. (Da Ihr execute Funktion tatsächlich synchron ist, sollte es keine Rolle.)

Zum Beispiel verwenden diese Methode von DispatchQueue:

func sync<T>(execute work:() throws -> T) rethrows -> T 

By the way, eine gute Idiom wenn verlässt ein DispatchGroup ist:

defer { group.leave() } 

als die erste Zeile Ihres Sync/async-Block, die Sie nicht versehentlich Deadlock garantiert werden wHE n ein Fehler passiert.

0

Sie können nicht einen Fehler aus, aber Sie können einen Fehler zurück:

Zuerst müssen Sie auch Ihre Berufung Funktion asynchron machen:

func execute(argument: someArg, completion: @escaping (Value?, Error?)->()) { 
    queue.async { 
    do { 
     // compute value here: 
     ... 
     completion(value, nil) 
    } catch { 
     completion(nil, error) 
    } 
    } 
} 

Der Abschluss Handler nimmt einen Parameter, wir konnten say ist ein "Ergebnis", das entweder den Wert oder einen Fehler enthält. Hier haben wir ein Tupel (Value?, Error?), wobei Value der Typ ist, der von der Task berechnet wird. Stattdessen könnten Sie dafür eine handlichere Swift Enum nutzen, z. Result<T> oder Try<T> (Sie möchten vielleicht im Internet suchen).

Dann verwenden Sie es wie folgt:

execute(argument: "Some string") { value, error in 
    guard error == nil else { 
     // handle error case 
    } 
    guard let value = value else { 
     fatalError("value is nil") // should never happen! 
    } 
    // do something with the value 
    ... 
} 

einige Regeln, die helfen könnten:

  1. Wenn eine Funktion intern eine asynchrone Funktion aufruft, ist es unvermeidlich als auch eine asynchrone Funktion wird. *)

  2. Eine asynchrone Funktion sollte einen Abschluss-Handler haben (sonst ist es eine Art "Feuer und Vergessen").

  3. Der Abschluss-Handler muss aufgerufen werden, egal was.

  4. muss die Abschluss-Handler asynchron (bezüglich dem die Anrufer) aufgerufen werden soll

  5. Der Abschluss-Handler auf einem privaten Ausführungskontext bezeichnet werden (auch bekannt als Absende-Warteschlange), sofern nicht die Funktion einen Parameter in dem ausführen zu spezifizieren hat der Beendigungshandler. Verwenden Sie niemals den Hauptthread oder die Hauptversandwarteschlange, es sei denn, Sie geben dies explizit in den Dokumenten an, oder Sie möchten absichtlich Deadlocks riskieren.

*) Sie können es zu machen, zwingen synchron Semaphore verwendet, die den aufrufenden Thread blockieren. Dies ist jedoch ineffizient und wird nur selten benötigt.

Nun, Sie könnten daraus schließen, dass dies etwas umständlich aussieht. Zum Glück gibt es Hilfe - Sie könnten nach Future oder Promise suchen, die dies schön umhüllen und den Code prägnanter und verständlicher machen können.

Hinweis: Im Komponententest würden Sie die Erwartungen verwenden, um asynchrone Aufrufe zu verarbeiten (siehe XCTest-Framework).

Verwandte Themen