2016-07-28 14 views
0

友人のリクエストを別のユーザーに送信するためのクリーンなJavascriptコードを書くために、Mongooseの組み込みサポートを利用しようとしています。しかし、適切なエラー処理と順序性を保証しようとすると、まだまだ正常よりも少し小さいピラミッドの運命に終わってしまいます。このマングースコードをどのようにお約束しますか?

ここでは、友人のリクエストが有効であることを確認してから、ターゲットのIDをリクエスタの送信リクエストに保存し、保存が成功した場合はリクエストのIDをターゲットのフレンドリクエストに保存します。

可能な限りきれいにするために、qのようなサードパーティのライブラリを使用する必要がありますか?最後に伝統的なシングルエラーハンドラを使用できるように、これをどのように構造化できますか?英語で

function _addFriend (requesterId, targetId) { 
// (integer, integer) 
User.findById(requesterId) 
.exec((requester) => { 
    if (!(targetId in requester.friends 
    || targetId in requester.sentfriendRequests 
    || targetId in requester.friendRequests)) { 
     requester.sentfriendRequests = requester.sentfriendRequests.concat([targetId]) 
     requester.save() 
     .then((err) => { 
     if (err) throw err; 
     User.findById(targetId) 
     .exec((err, target) => { 
      if (err) throw err; 
      target.friendRequests = target.friendRequests.concat([requesterId]) 
      target.save().then(err => {if (err) throw err}) 
      }) 
     }) 
    } 
}) 
} 
+0

最初の 'exec'コールバックに' err'パラメータは必要ないのですか? – Bergi

+0

'.then(err => {if(err)throw err})'約束でそれが必要なように見えない – Bergi

答えて

1

、これを行う方法は、それらにthenを追加し、thenブロックは約束、アンインデントを返す必要がありexec()によって返された約束を使用することです。私はアプリのロジックを読んで理解させるための@Bergiするためのコードで言う方がはるかに簡単... ​​

EDITおかげで(再び)。 @Bergiは、仕事を終わらせるにはちょっとネスティングする必要があるのは間違いないが、実際のポイントはネストを減らすことではなく、明快さを向上させることである。

約束を返すものも含め、論理部分に分解することで、より明確になります。

これらのいくつかの機能は、ロジックによって必要とされる約束のネスティングを隠しています。 (OPは、アプリがどのように処理するかを示すものではありませんので)これは、あなたはいくつかが必要になります...それが原因既存の要求にそうすることを拒否したときに返すべきものaddFriend

function _addFriend (requesterId, targetId) { 
    // note - pass no params to exec(), use it's returned promise 
    return User.findById(requesterId).exec().then((requester) => { 
     return canAddFriend(requester, targetId) ? addFriend(requester, targetId) : null; 
    }); 
} 

function canAddFriend(requester, targetId) { 
    return requester && targetId && 
     !(targetId in requester.friends 
      || targetId in requester.sentfriendRequests 
      || targetId in requester.friendRequests); 
} 

function addFriend(requester, targetId) { 
    requester.sentfriendRequests = requester.sentfriendRequests.concat([targetId]); 
    return requester.save().then(() => { 
     return User.findById(targetId).exec(); 
    }).then((target) => { 
     target.friendRequests = target.friendRequests.concat([requesterId]); 
     return target.save(); 
    }); 
} 
+0

あなたは 'doStuff'の後ろにある' 'body – Bergi

+0

ありがとう@Bergi、私はちょうど近いブレースを逃したと思います。あなたが言っていることではないかどうかは分かります。 – danh

+0

いいえ、中括弧を意味するわけではありませんでした。 'requester.save()'で始まるアクション全体を 'if'条件の中に入れる必要があります。 – Bergi

1

を指定していません有望なコードで条件を実行するようにネストされていますが、コールバックに基づくコードではそうではありません。

あなたはif (err) throw err;のものを少し台無しにしているようですが、約束でそれをする必要はありません。ただ常に.then(result => {…})を使用し、コールバックをexecに渡さないようにしてください。

returnは、非同期関数(連鎖の場合はthenコールバックを含む)から常に適切に約束すれば、最後に単一のエラーハンドラを追加できます。

function _addFriend (requesterId, targetId) { 
// (integer, integer) 
    return User.findById(requesterId).exec().then(requester => { 
     if (targetId in requester.friends 
      || targetId in requester.sentfriendRequests 
      || targetId in requester.friendRequests) { 
      return; 
     } 
     requester.sentfriendRequests = requester.sentfriendRequests.concat([targetId]) 
     return requester.save().then(() => { 
      return User.findById(targetId).exec() 
     }).then(target => { 
      target.friendRequests = target.friendRequests.concat([requesterId]) 
      return target.save() 
     }); 
    }); 
} 

_addFriend(…).catch(err => { 
    … 
}) 
0

あなたは.exec()は約束を返すことを認識すると、次のことができます。

  • は、望ましい平坦化を達成し、コードをより読みやすくします。
  • "成功"コードの間でエラーを処理する必要性を回避します。
  • 端末の.then()または.catch()エラーを処理します。

さらに、x in y条件ごとに意味のあるエラーが発生する(より簡単に)ことができます。

素直に、あなたが書くことができる:

function _addFriend(requesterId, targetId) { 
    return User.findById(requesterId).exec().then(requester => { 
     if (targetId in requester.friends) { 
      throw new Error('target is already a friend'); 
     } 
     if (targetId in requester.sentfriendRequests) { 
      throw new Error('friend request already sent to target'); 
     } 
     if (targetId in requester.friendRequests) { 
      throw new Error('target already sent a friend request to requester'); 
     } 
     requester.sentfriendRequests = requester.sentfriendRequests.concat([targetId]); // or just .push()? 
     return requester.save(); 
    }).then(() => { 
     return User.findById(targetId).exec().then(target => { 
      target.friendRequests = target.friendRequests.concat([requesterId]); // or just .push()? 
      return target.save(); 
     }); 
    }); 
} 

は、流れを制御するリターンの必要性に注意してください。

しかし、あなたはもっと良くすることができます。上記のように、要求されたものは成功し、ターゲットのものは失敗し、dbの不一致が生じます。だからあなたが本当に望むのは、dbトランザクションで、どちらも起こるかどうかを保証することではありません。 Mongooseは間違いなくトランザクションを提供しますが、部分利益のようなトランザクションを提供するためにクライアント側で何かを行うことができます。ここで

function _addFriend(requesterId, targetId) { 
    return Promise.all([User.findById(requesterId).exec(), User.findById(targetId).exec()]).then(([requester, target]) => { // note destructuring 
     if (targetId in requester.friends) { 
      throw new Error('target is already a friend'); 
     } 
     if (targetId in requester.sentfriendRequests) { 
      throw new Error('friend request already sent to target'); 
     } 
     if (targetId in requester.friendRequests) { 
      throw new Error('target already sent a friend request to requester'); 
     } 
     requester.sentfriendRequests = requester.sentfriendRequests.concat([targetId]); 
     target.friendRequests = target.friendRequests.concat([requesterId]); 
     return requester.save().then(() => { 
      return target.save(); 
     }); 
    }); 
} 

、あなたはまだ最初に保存すること(そう)の状況が成功すると保存第二に障害が発生したが、少なくとも、あなたが依頼者とターゲットの両方が存在しない限り、絶対に何も起こらない保証を持って得ることができます。どちらの場合も

次のように、呼び出し:

_addFriend(requesterId, targetId).then(function() { 
    // do whatever on success 
}, function(error) { 
    // do whatever on error 
}); 

をあなたはライブ環境でのエラーメッセージを使用しない場合でも/デバッグをテストするとき、彼らは非常に有用である可能性があります。それらをチェックしてください - 私はそれらを間違えたかもしれません。

関連する問題