2013-10-21 34 views
15

我還沒有完全理解承諾,所以如果這是一個簡單的誤解道歉。如何正確鏈接條件(?)承諾與Q.js

我有一個刪除頁面上的項目的功能,但我有一個具體的行爲取決於頁面的狀態。 Psuedo代碼明智的是這樣的:

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 

希望這是有道理的。基本上,如果有變化,數據必須先保存。然後,如果數據已保存,或者如果沒有更改,請提示用戶確認刪除。問題是我使用杜蘭達和微風,我似乎無法鏈接他們正確迴歸的承諾。

我的功能目前看起來像這樣,我知道這是錯誤的,但我努力找出解決的辦法。

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

從迪朗達爾的app.showMessage調用返回一個承諾,那麼this.save返回一個承諾,最後this.refresh也返回一個承諾。

所以我想我需要檢查hasChanges,然後如有必要調用保存並解決它。 然後,在該條件部分完成解析後,調用第二個提示,然後解決其中的所有承諾。

對不起,我不認爲這是超清晰的,但這也是我認爲來自我並不完全遵循這裏的鏈條的事實。

任何幫助非常感謝!謝謝。

回答

11

克里斯是正確的。您不需要任何Q.resolve調用。

順便說一句,返回承諾與解決的價值truefalse是沒有意義的在你的情況。我擔心你錯誤地認爲返回false會阻止鏈接then()被呼叫。並非如此!與false值被解析的承諾仍然是一個很好的承諾......如下面的代碼觸發警告消息框看到:

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

如果你想放的承諾處於故障狀態(和你不關心錯誤值),你應該返回Q.reject()


我不知道是什麼this是在你的代碼,但它的將是什麼,但是當你執行內部函數的麻煩。將其捕獲到一個變量中,這樣您就不會迷失方向並努力補償邏輯。


我不完全確定你要做什麼。看起來,如果有未保存的更改,您將不會繼續刪除項目。如果用戶確定,您將保存未保存的更改。然後你會要求用戶確認刪除。如果用戶拒絕保存掛起的更改,則不應該開始刪除過程。

如果我理解正確的,我想你想是這樣的:

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

很顯然,我還沒有測試此代碼。把它想成靈感。

+0

我已經標記了這個答案,因爲我基本上使用了所有這些代碼,但是就我所見,每個人的答案基本上都是正確的。 – Adam

+0

關於這個約定,我知道自我「模式」是應該如何完成的,但不幸的是,我發現了一種情況,我無法讓自我和封閉正常工作。然後我在發現綁定後修復了它,並且因爲它的工作而卡住了。不是很好。 – Adam

2

一般而言,您希望創建函數來完成您始終返回承諾的工作,即使這是一個立即解決的問題,即「返回Q.resolve(someData)」。

所以我會嘗試類似下面的內容。請注意下面額外的「返回」聲明。

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

所有這些Q.resolve調用都是不必要的。任何值都可以從promise處理程序返回並自動打包。 –

+1

厄運恐怖的承諾金字塔 – mumair

7

如果您在承諾中拋出錯誤,則該過程將直接跳至第一個.fail/.catch處理程序,其間跳過任何.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; 
     } 
    }); 
};