16

私はまだ promise を完全に理解していないので、これが単純な誤解である場合は申し訳ありません。

ページ上のアイテムを削除する機能がありますが、ページの状態に応じて特定の動作があります。疑似コード的には、次のようなものです。

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

durandal からの app.showMessage 呼び出しが promise を返し、次に this.save が promise を返し、最後に this.refresh も promise を返します。

したがって、hasChanges を確認し、必要に応じて save を呼び出して解決する必要があると思います。次に、その条件セクションの解決が完了したら、2 番目のプロンプトを呼び出し、その中のすべての promise を解決します。

申し訳ありませんが、これは非常に明確ではないと思いますが、それは、ここでチェーンを完全にフォローしていないという事実から来ていると思います.

どんな助けでも大歓迎です!ありがとう。

4

3 に答える 3

11

クリスは正しいです。Q.resolve 呼び出しは必要ありません。

ところで、解決された値を持つプロミスを返すtruefalse、あなたの状況では無意味です。戻るfalseとチェインthen()が呼び出されなくなるという誤った印象を受けているのではないかと思います。そうじゃない!の値を持つ解決された promise は、false依然として適切な promise です ... アラート メッセージ ボックスをトリガーする次のコードに見られるように:

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

promise を失敗した状態にしたい (そしてエラー値を気にしない) 場合は、 を返す必要がありますQ.reject()


コードの内容はわかりませんthisが、内部関数を実行すると問題が発生するだけです。変数にキャプチャして、迷子になったり、補正bind(this)ロジックに苦労したりしないようにします。


あなたが何をしようとしているのか完全にはわかりません。保存されていない変更がある場合、アイテムの削除を続行できないようです。ユーザーが OK すると、保存されていない変更を保存します。次に、ユーザーに削除の確認を求めます。ユーザーが保留中の変更を保存することを拒否した場合は、削除プロセスを開始することさえできません。

私の理解が正しければ、次のようなものが必要だと思います。

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

明らかに、私はこのコードをテストしていません。インスピレーションだと思ってください。

于 2013-10-22T09:32:48.377 に答える
7

promise でエラーをスローすると、プロセスは最初の .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;
        }
    });
};
于 2013-10-22T13:23:57.037 に答える
2

一般に、たとえそれがすぐに解決されたものであっても、常にプロミスを返す、つまり「return Q.resolve(someData)」という仕事をするための関数を作成したいと考えています。

だから私は次のようなものを試してみます。以下の追加の「return」ステートメントに注意してください。

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));
}
于 2013-10-21T23:39:57.187 に答える