2013-10-21 12 views
15

Ich habe noch nicht ganz ein vollständiges Verständnis der Versprechungen so Entschuldigungen, wenn das ein einfaches Missverständnis ist.Wie man bedingte (?) Versprechungen mit Q.js richtig kettet

Ich habe eine Funktion zum Löschen eines Elements auf einer Seite, aber ich habe ein bestimmtes Verhalten je nach dem Zustand der Seite. Psuedo Code-weise ist es so etwas wie folgt:

Hoffentlich macht das Sinn. Im Wesentlichen, wenn Änderungen vorliegen, müssen die Daten zuerst gespeichert werden. Wenn die Daten gespeichert wurden oder wenn anfangs keine Änderungen vorgenommen wurden, wird der Benutzer aufgefordert, den Löschvorgang zu bestätigen. Das Problem ist, dass ich Durandal und Brise benutze, und ich kann die Versprechen, die sie zusammenbringen, nicht korrekt verketten.

Meine Funktion sieht derzeit so aus, was ich weiß, ist falsch, aber ich kämpfe, um herauszufinden, wo es zu beheben ist.

if (this.hasChanges()) { 
    app.showMessage('Changes must be saved before removing external accounts. Would you like to save your changes now?', 'Unsaved Changes...', ['Yes', 'No']) 
     .then(function (selectedOption) { 
      if (selectedOption === 'Yes') { 
       return this.save(); 
      } else { 
       Q.resolve() 
      } 
     }); 
} 
app.showMessage('Are you sure you want to delete this item?', 'Delete?', ['Yes', 'No']) 
    .then(function (selectedOption) { 
     if (selectedOption === 'Yes') { 
      item.entityAspect.setDeleted(); 
      datacontext.saveChanges() 
       .then(function() { 
        logger.logNotificationInfo('Item deleted.', '', router.activeInstruction().config.moduleId); 
        Q.resolve(this.refresh(true)); 
       }.bind(this)); 
      } 
     }.bind(this)); 

Der app.showMessage Anruf von durandal gibt ein Versprechen, dann ist die this.save gibt ein Versprechen, und schließlich die This.Refresh gibt auch ein Versprechen.

Also ich denke, ich muss die hasChanges überprüfen, dann bei Bedarf aufrufen speichern und lösen. Nachdem der bedingte Abschnitt die Auflösung beendet hat, rufen Sie die zweite Eingabeaufforderung auf und lösen Sie alle darin enthaltenen Versprechen.

Es tut mir leid, ich glaube nicht, dass das super klar ist, aber das ist auch, denke ich, dass ich hier nicht ganz den Ketten folge.

Jede Hilfe sehr geschätzt! Vielen Dank.

Antwort

11

Kris ist korrekt. Sie benötigen keine Q.resolve-Aufrufe.

Btw, ein Versprechen mit aufgelöstem Wert true oder false ist in Ihrer Situation bedeutungslos. Ich fürchte, Sie sind unter dem falschen Eindruck, dass die Rückkehr false verhindern würde, dass der verkettete then() aufgerufen wird. Nicht so! Ein Versprechen gelöst mit einem Wert von false ist immer noch ein gutes Versprechen ... wie im folgenden Code zu sehen, die die Warnmeldung Feld löst:

Q(false) // same as Q.resolve(false) 
.then(function() { alert('resolve(false) triggered then()') }) 

Wenn Sie das Versprechen, in einem ausgefallenen Zustand setzen wollen (und Sie den Fehlerwert nicht interessieren), sollten Sie Q.reject() zurückgeben.


Ich weiß nicht, was this in Ihrem Code, aber es aber Schwierigkeiten sein nichts los ist, wie Sie die inneren Funktionen auszuführen. Erfassen Sie es in einer Variablen, so dass Sie nicht verloren gehen und kämpfen mit der Logik bind(this) Logik zu kompensieren.


Ich bin mir nicht ganz sicher, was Sie versuchen zu tun. Es scheint, dass Sie nicht mit dem Löschen eines Elements fortfahren, solange ungespeicherte Änderungen vorliegen.Sie speichern nicht gespeicherte Änderungen, wenn der Benutzer dies bestätigt. Dann werden Sie den Benutzer bitten, das Löschen zu bestätigen. Wenn der Benutzer ausstehende Änderungen nicht speichern möchte, sollten Sie den Löschvorgang noch nicht einmal starten.

Wenn ich richtig verstehe, ich glaube, Sie so etwas wie dies wollen:

var self = this; // WHAT IS THIS? I don't know but capture it as 'self' 

function saveBeforeDeleting() { 
    return saveIfNeeded().then(deleteIfConfirmed); 
} 

function saveIfNeeded() { 
    // no need to save; return resolved promise 
    if (!self.hasChanges()) return Q(); 

    var dialogPromise = app.showMessage(
    'Changes must be saved before removing external accounts. '+ 
    'Would you like to save your changes now?', 
    'Unsaved Changes...', ['Yes', 'No'] 
); 

    // When the user replies, either save or return a rejected promise 
    // (which stops the flow) 
    return dialogPromise.then(function (selectedOption) { 
    return (selectedOption === 'Yes') ? self.save() : Q.reject(); 
    }); 
} 

function deleteIfConfirmed() { 
    var dialogPromise = app.showMessage(
    'Are you sure you want to delete this item?', 
    'Delete?', 
    ['Yes', 'No'] 
); 

    return dialogPromise.then(function (selectedOption) { 
    return (selectedOption === 'Yes') ? deleteIt() : Q.reject(); 
    }); 

    function deleteIt() { 
    item.entityAspect.setDeleted(); 
    return datacontext.saveChanges().then(logAndRefresh); 
    } 

    function logAndRefresh() { 
    logger.logNotificationInfo(
     'Item deleted.', 
     '', 
     router.activeInstruction().config.moduleId 
    ); 
    return self.refresh(true)); 
    } 
} 

Offensichtlich habe ich diesen Code nicht getestet. Betrachten Sie es als Inspiration.

+0

Ich habe dies als die Antwort markiert, weil ich im Grunde genommen all diesen Code verwendet, aber die Antwort von allen war im Wesentlichen richtig, soweit ich sehen konnte. – Adam

+0

In Bezug auf diese Konvention weiß ich, dass das "Muster" so ist, wie es gemacht werden soll, aber leider habe ich eine Situation gefunden, in der ich das Selbst und die Schließungen nicht richtig funktionieren lassen konnte. Dann habe ich es repariert, nachdem ich etwas über Bind herausgefunden hatte, und es blieb einfach hängen, weil es funktionierte. Nicht gut. – Adam

2

Im Allgemeinen möchten Sie Funktionen für Ihre Arbeit erstellen, die IMMER eine Zusage zurückgeben, selbst wenn es sich um eine sofort aufgelöste handelt, d. H. "Return Q.resolve (someData)".

Also würde ich etwas wie das folgende versuchen. Beachten Sie die zusätzlichen "Return" -Anweisungen unten.

function complexSave() { 
    return saveIfNeeded().then(confirmDelete); 
} 

// returns a promise 
function saveIfNeeded() { 
    if (this.hasChanges()) { 
    return app.showMessage('Changes must be saved before removing external accounts. Would you like to save your changes now?', 'Unsaved Changes...', ['Yes', 'No']). 
     then(function (selectedOption) { 
     if (selectedOption === 'Yes') { 
      return this.save(); 
     } else { 
      return Q.resolve(false) 
     } 
    }); 
    else { 
    return Q.resolve(false); 
    } 
} 

// returns a promise 
function confirmDelete() { 
    return app.showMessage('Are you sure you want to delete this item?', 'Delete?', ['Yes', 'No']) 
    .then(function (selectedOption) { 
     if (selectedOption === 'Yes') { 
      item.entityAspect.setDeleted(); 
      return datacontext.saveChanges() 
      .then(function() { 
       logger.logNotificationInfo('Item deleted.', '', router.activeInstruction().config.moduleId); 
       return Q.resolve(this.refresh(true)); 
      }.bind(this)); 
     } else { 
      return Q.resolve(false); 
     } 
    }.bind(this)); 
} 
+3

Alle diese Q.Resolve-Aufrufe sind nicht erforderlich. Jeder Wert kann von einem Promise-Handler zurückgegeben werden und wird automatisch umbrochen. –

+1

schreckliche Promise Pyramide des Untergangs – mumair

7

Wenn Sie einen Fehler in ein Versprechen werfen, springt der Prozess direkt zum ersten .fail/.catch-Handler und überspringt alle dazwischen liegenden .thens().

function AbortError() {} 

MyClass.prototype.delete = function() { 
    var p = Q(); 
    var self = this; 
    if(this.hasChanges()) { 
     p = app.showMessage('...', '...', ['Yes', 'No']) 
     .then(function(answer){ 
      if(answer === "Yes") { 
       return self.save(); //I assume save returns a promise 
      } 
      throw new AbortError(); 
     }); 
    } 
    return p 
    .then(function() { 
     return app.showMessage('...', '...', ['Yes', 'No']) 
    }) 
    .then(function(answer) { 
     if(answer === "yes") { 
      item.entityAspect.setDeleted(); 
      return datacontext.saveChanges(); 
     } 
     throw new AbortError(); 
    }) 
    .then(function(){ 
     logger.logNotificationInfo('Item deleted.', '', router.activeInstruction().config.moduleId); 
     self.refresh(true); 
    }) 
    .fail(function(e){ 
     //kris please provide typed .catch feature :(
     if(!(e instanceof AbortError)) { 
      throw e; 
     } 
    }); 
};