約束を返すこの関数を認証させるとしましょう。その結果、約束が解決します。私が見るように、falseとtrueは期待される結果であり、拒否はエラーの場合にのみ発生するはずです。または、認証の失敗は、約束を拒否する何かと見なされますか?
false
か、例外をスローするように扱いますか?
約束を返すこの関数を認証させるとしましょう。その結果、約束が解決します。私が見るように、falseとtrueは期待される結果であり、拒否はエラーの場合にのみ発生するはずです。または、認証の失敗は、約束を拒否する何かと見なされますか?
false
か、例外をスローするように扱いますか?
回答:
良い質問!難しい答えはありません。それはあなたがフローのその特定のポイントで例外的であると考えるものによります。
aを拒否することPromise
は、例外を発生させることと同じです。すべての望ましくない結果が例外であるというわけではなく、エラーの結果です。あなたは両方の方法であなたのケースを主張することができます:
失敗した認証べき、呼び出し側が期待しているので、お返しにオブジェクトを、そして何かがこのフローの例外です。reject
Promise
User
間違った資格情報を提供することは実際には例外的なケースではないため、認証に失敗しresolve
たPromise
としても、呼び出し側はフローが常にになることを期待すべきではありません。null
User
私は問題を発信者側から見ていることに注意してください。情報の流れの中で、呼び出し元は自分のアクションが結果をもたらすと期待しUser
ていますか(それ以外はエラーです)、この特定の呼び出し元が他の結果を処理するのは理にかなっていますか?
多層システムでは、データが層を通過するにつれて答えが変わる場合があります。例えば:
null
ユーザーに解決されるため、エラーをキャッチします。この4点の例は明らかに複雑ですが、2点を示しています。
繰り返しますが、難しい答えはありません。考えて設計する時間です!
したがって、Promiseには関数型言語からJSをもたらすという優れた特性があります。つまり、ロジックに1つのブランチまたは他のブランチのいずれかを強制することによりEither
、Left
タイプとタイプという2つの他のタイプを結合するこのタイプコンストラクターを実際に実装Right
しますブランチ。
data Either x y = Left x | Right y
さて、あなたは実際、左側の型が約束に対して曖昧であることに気づいています。何でも拒否できます。これは、JSの型指定が弱いため当てはまりますが、防御的なプログラミングを行う場合は注意が必要です。
その理由は、JSがthrow
プロミス処理コードからステートメントを取得し、そのLeft
サイドにバンドルするからです。技術的にJSであなたのことができthrow
、真/の虚偽または文字列または数字を含め、すべてのものを:しかし、JavaScriptコードもせずに物事をスローthrow
(あなたはヌルにアクセスプロパティしようとしているようなことを行うとき)、このための定住API(そこにあるError
オブジェクト) 。したがって、キャッチに取り掛かるとき、通常、それらのエラーがError
オブジェクトであると想定できると便利です。またreject
、上記のバグのいずれかのエラーでプロミスが凝集するため、通常は、throw
他のエラーのみを使用して、catch
文にシンプルで一貫したロジックを持たせます。
したがって、if-conditionalをに入れて、誤ったエラーを探すことができますがcatch
、その場合、真実のケースは簡単です。
Either (Either Error ()) ()
少なくともオーセンティケーターからすぐに出てくるものについては、より単純なブール値の論理構造を好むでしょう。
Either Error Bool
実際、次のレベルの認証ロジックはおそらくUser
、認証されたユーザーを含む何らかのオブジェクトを返すことになるため、次のようになります。
Either Error (Maybe User)
そして、これは多かれ少なかれ私が期待するものです:null
ユーザーが定義されていない場合に戻り、そうでなければreturn {user_id: <number>, permission_to_launch_missiles: <boolean>}
。ログインしていないという一般的なケースは、たとえば「新しいクライアントへのデモ」モードの場合など、救助可能であり、誤ってobject.doStuff()
いつobject.doStuff
に呼び出されたバグと混同しないでくださいundefined
。
さて、それで、あなたがしたいことはから派生するNotLoggedIn
or PermissionError
例外を定義することですError
。次に、本当にそれを必要とするものであなたが書きたい:
function launchMissiles() {
function actuallyLaunchThem() {
// stub
}
return getAuth().then(auth => {
if (auth === null) {
throw new PermissionError('Cannot launch missiles without permission, cannot have permission if not logged in.');
} else if (auth.permission_to_launch_missiles) {
return actuallyLaunchThem();
} else {
throw new PermissionError(`User ${auth.user_id} does not have permission to launch the missiles.`);
}
});
}
エラーについて話しましょう。
エラーには2つのタイプがあります。
予期されるエラーとは、間違ったことが発生する状態ですが、その可能性があることはわかっているため、対処します。
これらは、ユーザー入力やサーバー要求などです。ユーザーが間違いを犯したり、サーバーがダウンしたりする可能性があることを知っているので、チェックコードを記述して、プログラムが入力を再度要求するか、メッセージを表示するか、その他の適切な動作を確認します。
これらは処理されると回復可能です。未処理のままにすると、予期しないエラーになります。
予期しないエラー(バグ)は、コードが間違っているために間違ったことが起こる状態です。それらは最終的には発生することを知っていますが、定義により予想外であるため、どこでどのように対処するかを知る方法はありません。
これらは、構文エラーや論理エラーなどです。コードにタイプミスがあり、間違ったパラメーターで関数を呼び出した可能性があります。通常、これらは回復できません。
try..catch
について話しましょうtry..catch
。
JavaScriptでは、throw
一般的に使用されません。コード内の例を見てみると、それらはほとんどないでしょうし、通常は
function example(param) {
if (!Array.isArray(param) {
throw new TypeError('"param" should be an array!');
}
...
}
このため、try..catch
制御フローではブロックもそれほど一般的ではありません。通常、メソッドを呼び出す前にいくつかのチェックを追加して、予期されるエラーを回避するのは非常に簡単です。
JavaScript環境もかなり寛容なので、予期しないエラーもしばしばキャッチされずに残されます。
try..catch
珍しいことはありません。JavaやC#などの言語ではより一般的な、いくつかの便利な使用例があります。JavaとC#には型付きcatch
構造の利点があるため、予想されるエラーと予期しないエラーを区別できます。
try
{
var example = DoSomething();
}
catch (ExpectedException e)
{
DoSomethingElse(e);
}
この例では、他の予期しない例外を上に流して、他の場所で処理できます(ログに記録してプログラムを閉じるなど)。
JavaScriptでは、この構成は次の方法で複製できます。
try {
let example = doSomething();
} catch (e) {
if (e instanceOf ExpectedError) {
DoSomethingElse(e);
} else {
throw e;
}
}
それほどエレガントではありませんが、これは珍しい理由の一部です。
関数について話しましょう。
単一の責任原則を使用する場合、各クラスと関数は単一の目的を果たす必要があります。
たとえばauthenticate()
、ユーザーを認証する場合があります。
これは次のように記述できます。
const user = authenticate();
if (user == null) {
// keep doing stuff
} else {
// handle expected error
}
または、次のように記述される場合があります。
try {
const user = authenticate();
// keep doing stuff
} catch (e) {
if (e instanceOf AuthenticationError) {
// handle expected error
} else {
throw e;
}
}
どちらも受け入れられます。
約束について話しましょう。
Promiseはの非同期形式ですtry..catch
。コードを呼び出すnew Promise
かPromise.resolve
、try
コードを開始します。呼び出すthrow
かPromise.reject
、catch
コードを送信します。
Promise.resolve(value) // try
.then(doSomething) // try
.then(doSomethingElse) // try
.catch(handleError) // catch
ユーザーを認証する非同期関数がある場合、次のように記述できます。
authenticate()
.then((user) => {
if (user == null) {
// keep doing stuff
} else {
// handle expected error
}
});
または、次のように記述される場合があります。
authenticate()
.then((user) => {
// keep doing stuff
})
.catch((e) => {
if (e instanceOf AuthenticationError) {
// handle expected error
} else {
throw e;
}
});
どちらも受け入れられます。
ネスティングについて話しましょう。
try..catch
ネストできます。あなたのauthenticate()
方法は、内部的に持っているかもしれないtry..catch
次のようなブロックを
try {
const credentials = requestCredentialsFromUser();
const user = getUserFromServer(credentials);
} catch (e) {
if (e instanceOf CredentialsError) {
// handle failure to request credentials
} else if (e instanceOf ServerError) {
// handle failure to get data from server
} else {
throw e; // no idea what happened
}
}
同様に、promiseはネストできます。非同期authenticate()
メソッドは、内部的にプロミスを使用する場合があります。
requestCredentialsFromUser()
.then(getUserFromServer)
.catch((e) => {
if (e instanceOf CredentialsError) {
// handle failure to request credentials
} else if (e instanceOf ServerError) {
// handle failure to get data from server
} else {
throw e; // no idea what happened
}
});
さて、質問に実際に答える時が来たと思います。
認証の失敗は、約束を拒否する何かと考えられますか?
私ができる最も簡単な答えは、throw
同期コードである場合は例外を避けたいと思うところならどこでも約束を拒否すべきだということです。
ステートメントにいくつif
かのチェックをthen
入れることで制御フローがより簡単になった場合、約束を拒否する必要はありません。
約束を拒否し、エラー処理コードでエラーのタイプをチェックすることで制御フローがより単純な場合は、代わりにそれを実行します。
約束の処理は、「if」条件に似ています。認証が失敗した場合に「解決」するか「拒否」するかは、あなた次第です。
try..catch
、ではありませんif
。
try...catch
完了して結果を得ることができた場合は、受け取った値に関係なく解決する必要があると単純に言う必要があります。そうでなければ拒否する必要がありますか?
try { if (!doSomething()) throw whatever; doSomethingElse() } catch { ... }
まったく問題ありませんPromise
が、aが表す構造はtry..catch
部分であり、部分ではありませんif
。
doSomething()
失敗した場合はスローするという単純なものですが、そうでない場合は必要な値が含まれている可能性があります(ここでのアイデアの一部ではないため、if
上記は少し混乱しています:))。テストが失敗した場合、スローする理由がある場合(類推で)のみ拒否する必要があります。テストが成功した場合、その値が正であるかどうかに関係なく、常に解決する必要がありますよね?
reject
falseを返す必要がありますが、値がaBool
であると予想される場合は成功し、値に関係なくBoolで解決する必要があります。Promiseは値の一種のプロキシです-返された値を保存するため、値を取得できなかった場合にのみを使用してくださいreject
。それ以外の場合はする必要がありresolve
ます。