简体   繁体   English

如何使用Q.js正确链接条件(?)promise

[英]How to correctly chain conditional(?) promises with Q.js

I've still not quite got a complete understanding of promises so apologies if this is a simple misunderstanding. 如果这是一个简单的误解,我还没有完全理解承诺,所以道歉。

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: 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

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调用返回一个promise,然后this.save返回一个promise,最后this.refresh也返回一个promise。

So I guess I need to check the hasChanges, then if necessary call save, and resolve it. 所以我想我需要检查hasChanges,然后在必要时调用save,然后解决它。 Then after that conditional section has finished resolving, call the second prompt, and then resolve all the promises within that. 然后在该条件部分完成解析后,调用第二个提示,然后解析其中的所有承诺。

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. 谢谢。

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. 顺便说一句,在你的情况下,返回一个具有已解决价值truefalse的承诺是毫无意义的。 I fear you are under the mistaken impression that returning false would prevent the chained then() from being called. 我担心你会错误地认为返回false会阻止链接then()被调用。 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: 值为false已解决的promise仍然是一个很好的承诺......如以下代码中所示,它触发警报消息框:

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. 我不知道this是在你的代码,但它的将是什么,但是当你执行内部函数的麻烦。 Capture it in a variable so you don't get lost and struggle with compensating bind(this) logic. 在一个变量中捕获它,这样你就不会迷失并且在补偿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. 把它想象成灵感。

If you throw an error in a promise, the process will jump straight to the first .fail/.catch handler skipping any .thens() in between. 如果你在promise中抛出一个错误,那么进程将直接跳转到第一个.fail / .catch处理程序,跳过.thens()任何.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;
        }
    });
};

In general you want to create functions to do your work that ALWAYS return a promise, even if that is an immediately resolved one, ie "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));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM