2013-10-21 14 views
15

Non ho ancora avuto una comprensione completa delle promesse così scuse se questo è un semplice fraintendimento.Come concatenare le promesse condizionali (?) Con Q.js

Ho una funzione per eliminare un elemento in una pagina ma ho un comportamento specifico a seconda dello stato della pagina. Psuedo in termini di codice è qualcosa del genere:

Does the page have changes? 
    If yes - prompt to save changes first 
     If yes - save changes 
     If no - exit function 
    If no - continue 
Prompt to confirm delete 
    If yes - delete item and reload data 
    If no - exit function 

Speriamo che abbia senso. In sostanza se ci sono cambiamenti, i dati devono essere salvati per primi. Quindi, se i dati sono stati salvati o se non sono state apportate modifiche, richiedere all'utente di confermare l'eliminazione. Il problema è che sto usando durandal e brezza, e non riesco a pareggiare le promesse che ritornano insieme correttamente.

Attualmente la mia funzione è simile a questa, che a mio avviso è errata, ma non riesco a capire dove risolverlo.

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

La chiamata app.showMessage da Durandal restituisce una promessa, poi il this.save restituisce una promessa, e, infine, il This.Refresh restituisce anche una promessa.

Quindi suppongo di dover controllare le haschanges, quindi se necessario chiamare save e risolverlo. Quindi dopo che la sezione condizionale ha terminato la risoluzione, chiama il secondo prompt, quindi risolvi tutti i promessi all'interno di quello.

Mi dispiace, non penso che questo sia chiarissimo, ma penso anche che provenga dal fatto che non sto seguendo completamente le catene qui.

Qualsiasi aiuto molto apprezzato! Grazie.

risposta

11

Kris è corretto. Non avrai bisogno di nessuna delle chiamate Q.resolve.

Btw, restituire una promessa con valore risolto true o false non ha senso nella vostra situazione. Temo che tu abbia l'impressione sbagliata che la restituzione di false impedisca di chiamare la catena then(). Non così! Una promessa risolto con un valore di false è ancora una buona promessa ... come si vede nel seguente codice che attiva la finestra di messaggio di avviso:

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

Se si vuole mettere la promessa in stato di errore (e voi non interessa il valore dell'errore), è necessario restituire Q.reject().


Non so cosa this è nel codice, ma sarà solo guai, come si esegue le funzioni interne. Catturalo in una variabile in modo da non perderti e lottare con la compensazione della logica bind(this).


Non sono del tutto sicuro di quello che stai cercando di fare. Sembra che non procederai con l'eliminazione di un elemento mentre ci sono modifiche non salvate.Salvi le modifiche non salvate se l'utente lo conferma. Quindi chiederai all'utente di confermare l'eliminazione. Se l'utente rifiuta di salvare le modifiche in sospeso, non si dovrebbe nemmeno iniziare il processo di cancellazione.

Se ho capito bene, penso che si desidera qualcosa di simile:

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

Ovviamente non ho ancora testato questo codice. Pensala come ispirazione.

+0

Ho contrassegnato questo come la risposta perché ho praticamente usato tutto questo codice, ma la risposta di tutti era sostanzialmente corretta per quanto ho potuto vedere. – Adam

+0

Per quanto riguarda questa convenzione, so che il "modello" di sé è come dovrebbe essere fatto, ma sfortunatamente ho trovato una situazione in cui non riuscivo a far sì che il sé e le chiusure funzionassero correttamente. Poi l'ho risolto dopo aver scoperto il legame, e si è bloccato perché funzionava. Non buono. – Adam

2

In generale, si desidera creare funzioni per eseguire il proprio lavoro che SEMPRE restituiscano una promessa, anche se quella è immediatamente risolta, ovvero "restituire Q.resolve (someData)".

Quindi proverei qualcosa come il seguente. Nota le dichiarazioni extra di "ritorno" qui sotto.

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

Tutte queste chiamate Q.resolve non sono necessarie. Qualsiasi valore può essere restituito da un gestore di promessa e verrà automaticamente incapsulato. –

+1

piramide orribile promessa di sventura – mumair

7

Se si genera un errore in una promessa, il processo passerà direttamente al primo gestore .fail/.catch saltando uno qualsiasi tra .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; 
     } 
    }); 
};