javascript 如何使用 Q.js 正确链接条件(?)承诺
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19506163/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to correctly chain conditional(?) promises with Q.js
提问by Adam
I've still not quite got a complete understanding of promises so apologies if this is a simple misunderstanding.
我还没有完全理解 promise,所以如果这是一个简单的误解,我深表歉意。
I have a function for deleting an item on a page but I have a specific behaviour depending on the state of the page. Psuedo code-wise it's something like this:
我有一个删除页面上项目的功能,但我有一个特定的行为取决于页面的状态。伪代码是这样的:
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
Hopefully that makes sense. Essentially if there are changes, the data must be saved first. Then if the data has been saved, or if there were no changes to begin with, prompt the user to confirm deletion. The problem is I'm using durandal and breeze, and I can't seem to chain the promises they return together correctly.
希望这是有道理的。本质上,如果有更改,必须先保存数据。然后,如果数据已保存,或者如果开始时没有更改,则提示用户确认删除。问题是我正在使用 durandal 和微风,我似乎无法将它们正确返回的承诺链接在一起。
My function currently looks like this, which I know is wrong, but I'm struggling to work out where to fix it.
我的函数目前看起来像这样,我知道这是错误的,但我正在努力找出在哪里修复它。
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));
The app.showMessage call from durandal returns a promise, then the this.save returns a promise, and finally the this.refresh also returns a promise.
来自 durandal 的 app.showMessage 调用返回一个承诺,然后 this.save 返回一个承诺,最后 this.refresh 也返回一个承诺。
So I guess I need to check the hasChanges, then if necessary call save, and resolve it. Then after that conditional section has finished resolving, call the second prompt, and then resolve all the promises within that.
所以我想我需要检查hasChanges,然后在必要时调用保存并解决它。然后在该条件部分完成解析后,调用第二个提示,然后解析其中的所有承诺。
I'm sorry I don't think this is super clear, but that's also I think coming from the fact I'm not completely following the chains here.
对不起,我认为这不是很清楚,但这也是我认为来自我没有完全遵循这里的链条的事实。
Any help much appreciated! Thanks.
非常感谢任何帮助!谢谢。
采纳答案by Ward
Kris is correct. You won't need any of the Q.resolve calls.
克里斯是对的。您不需要任何 Q.resolve 调用。
Btw, returning a promise with resolved value true
or false
is meaningless in your situation. I fear you are under the mistaken impression that returning false
would prevent the chained then()
from being called. Not so! A resolved promise with a value of false
is still a good promise ... as seen in the following code which triggers the alert message box:
顺便说一句,返回具有已解决价值的承诺true
或false
在您的情况下毫无意义。我担心你会错误地认为返回false
会阻止被锁链then()
的被调用。不是这样!具有值的false
已解决的承诺仍然是一个好的承诺......如以下触发警报消息框的代码所示:
Q(false) // same as Q.resolve(false)
.then(function () { alert('resolve(false) triggered then()') })
If you want to put the promise in a failed state (and you don't care about the error value), you should return Q.reject()
.
如果您想将 Promise 置于失败状态(并且您不关心错误值),您应该返回Q.reject()
.
I don't know what this
is in your code but it's going to be nothing but trouble as you execute the inner functions. Capture it in a variable so you don't get lost and struggle with compensating bind(this)
logic.
我不知道this
您的代码中有什么,但是当您执行内部函数时,它只会带来麻烦。将其捕获在一个变量中,这样您就不会迷路并为补偿bind(this)
逻辑而苦恼。
I'm not entirely sure what you're trying to do. It appears that you won't proceed with deleting an item while there are unsaved changes. You'll save unsaved changes if the user OKs that. Then you'll ask the user to confirm the delete. If the user refuses to save pending changes, you should not even begin the delete process.
我不完全确定你想要做什么。当有未保存的更改时,您似乎不会继续删除项目。如果用户同意,您将保存未保存的更改。然后您将要求用户确认删除。如果用户拒绝保存挂起的更改,您甚至不应该开始删除过程。
If I understand correctly, I think you want something like 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));
}
}
Obviously I haven't tested this code. Think of it as inspiration.
显然我还没有测试过这段代码。将其视为灵感。
回答by Esailija
If you throw an error in a promise, the process will jump straight to the first .fail/.catch handler skipping any .thens()
in between.
如果您在承诺中抛出错误,则该过程将直接跳转到第一个 .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;
}
});
};
回答by Jay Traband
In general you want to create functions to do your work that ALWAYS return a promise, even if that is an immediately resolved one, i.e. "return Q.resolve(someData)".
通常,您希望创建函数来完成始终返回承诺的工作,即使这是立即解决的承诺,即“返回 Q.resolve(someData)”。
So I'd try something like the following. Note the extra "return" statements below.
所以我会尝试类似以下的东西。请注意下面额外的“返回”语句。
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));
}