2016-06-20 9 views
4

Ich baue eine Swift-basierte iOS-Anwendung, die PromiseKit verwendet, um Versprechungen zu behandeln (obwohl ich offen bin, die Versprechens-Bibliothek zu wechseln, wenn es mein Problem leichter zu lösen macht). Es gibt einen Codeabschnitt, der entwickelt wurde, um Fragen zum Überschreiben von Dateien zu behandeln.Wie kann ich Versprechen in Swift innerhalb einer Schleife verketten?

Ich habe Code, der in etwa wie folgt aussieht:

let fileList = [list, of, files, could, be, any, length, ...] 

for file in fileList { 
    if(fileAlreadyExists) { 
    let overwrite = Promise<Bool> { fulfill, reject in 
     let alert = UIAlertController(message: "Overwrite the file?") 
     alert.addAction(UIAlertAction(title: "Yes", handler: { action in 
     fulfill(true) 
     } 
     alert.addAction(UIAlertAction(title: "No", handler: { action in 
     fulfill(false) 
     } 
    } else { 
     fulfill(true) 
    } 
    } 

    overwrite.then { result -> Promise<Void> in 
    Promise<Void> { fulfill, reject in 
     if(result) { 
     // Overwrite the file 
     } else { 
     // Don't overwrite the file 
     } 
    } 
} 

Dies ist jedoch nicht die gewünschte Wirkung; Die for-Schleife wird so schnell ausgeführt, wie es nötig ist, um über die Liste zu iterieren, was bedeutet, dass UIAlertController beim Versuch, eine Frage auf eine andere zu überlagern, verwirrt wird. Was ich will, ist für die Versprechen, zu verketten, so dass erst, wenn der Benutzer "Ja" oder "Nein" ausgewählt hat (und der nachfolgende "überschreiben" - oder "nicht überschreiben" -Code ausgeführt hat), die nächste Iteration der for Schleife passieren. Im Wesentlichen möchte ich, dass die gesamte Sequenz sequenziell ist.

Wie kann ich diese Versprechen verketten, wenn man bedenkt, dass das Array von unbestimmter Länge ist? Ich habe das Gefühl, dass ich etwas Offensichtliches vermisse.

Bearbeiten: eine der folgenden Antworten schlägt Rekursion vor. Das klingt vernünftig, obwohl ich mir nicht sicher bin, welche Auswirkungen dies auf Swifts Stack haben wird (das ist in einer iOS-App), wenn die Liste länger wird. Ideal wäre, wenn es ein Konstrukt gäbe, um dies natürlicher zu machen, indem man das Versprechen verkettet.

Antwort

0

Ein Ansatz: Erstellen Sie eine Funktion, die eine Liste der verbleibenden Objekte aufnimmt. Verwenden Sie das als Rückruf in then. In Pseudo-Code:

function promptOverwrite(objects) { 
    if (objects is empty) 
     return 
    let overwrite = [...] // same as your code 
    overwrite.then { 
     do positive or negative action 
     // Recur on the rest of the objects 
     promptOverwrite(objects[1:]) 
    } 
} 

Jetzt könnten wir auch in dies zu tun, ohne Rekursion interessiert sein, nur die Call-Stack Blasen zu vermeiden, wenn wir Zehntausende von Versprechungen haben. (Angenommen, die Versprechen erfordern keine Benutzerinteraktion, und alle lösen sich in der Größenordnung von einigen Millisekunden ab, so dass das Szenario realistisch ist).

Beachten Sie zuerst, dass der Rückruf im then -Happen im Zusammenhang mit einem Abschluss, so dass es nicht mit einem der äußeren Kontrollfluss interagieren kann, wie erwartet. Wenn wir keine Rekursion verwenden wollen, müssen wir wahrscheinlich andere native Funktionen nutzen.

Der Grund, warum Sie Versprechungen in erster Linie verwenden, ist vermutlich, dass Sie (weislich) den Hauptthread nicht blockieren möchten. Betrachten wir nun einen zweiten Thread, dessen einziger Zweck es ist, diese Versprechen zu orchestrieren. Wenn Ihre Bibliothek explizit erlaubt, für ein Versprechen zu warten, tun nur so etwas wie

function promptOverwrite(objects) { 
    spawn an NSThread with target _promptOverwriteInternal(objects) 
} 
function _promptOverwriteInternal(objects) { 
    for obj in objects { 
     let overwrite = [...] // same as your code 
     overwrite.then(...) // same as your code 
     overwrite.awaitCompletion() 
    } 
} 

Wenn Ihr Versprechen Bibliothek nicht Sie lassen Sie sich dies, könnte man es umgehen, indem eine Sperre mit:

function _promptOverwriteInternal(objects) { 
    semaphore = createSemaphore(0) 
    for obj in objects { 
     let overwrite = [...] // same as your code 
     overwrite.then(...) // same as your code 
     overwrite.always { 
      semaphore.release(1) 
     } 
     semaphore.acquire(1) // wait for completion 
    } 
} 
+0

Entschuldigung für den Pseudocode - ich kenne Swift nicht. Aber das ist eine gute Frage, und ich denke, die Lösung ist ziemlich sprachunabhängig. – wchargin

+0

Fühlen Sie sich frei zu bearbeiten und fügen Sie tatsächliche Swift! :) – wchargin

+0

Im Wesentlichen schlagen Sie vor, Rekursion zu verwenden, denke ich. Das ist eine vernünftige Idee, wenn die Liste kurz ist. Ich bin ein bisschen besorgt wegen der Swift-Implikationen, wenn es länger wird (ich bin nicht genug von einem Swift-Experten, um zu wissen, ob das ernsthafte Konsequenzen hätte). –

Verwandte Themen