私はこの主題に関するいくつかの記事を読みましたが、Promise.reject
とエラーを投げることの間に違いがあるかどうかはまだ私には明らかではありません。例えば、
Promise.rejectを使う
return asyncIsPermitted()
.then(function(result) {
if (result === true) {
return true;
}
else {
return Promise.reject(new PermissionDenied());
}
});
throwを使う
return asyncIsPermitted()
.then(function(result) {
if (result === true) {
return true;
}
else {
throw new PermissionDenied();
}
});
私は私の好みはthrow
を使うことです。なぜならそれはもっと短いからですが、一方にもう一方よりも利点があるのかどうか疑問に思いました。
一方と他方を使用する利点はありませんが、throw
が機能しない特定のケースがあります。ただし、これらのケースは修正できます。
Promiseコールバック内にいるときはいつでも、throw
を使用できます。ただし、他の非同期コールバックを使用している場合は、reject
を使用する必要があります。
例えば、
new Promise(function() {
setTimeout(function() {
throw 'or nah';
// return Promise.reject('or nah'); also won't work
}, 1000);
}).catch(function(e) {
console.log(e); // doesn't happen
});
キャッチを引き起こすことはありません、代わりに未解決の約束とキャッチされていない例外が残されます。それはreject
を代わりに使いたい場合です。ただし、タイムアウトを確認することでこれを修正できます。
function timeout(duration) { // Thanks joews
return new Promise(function(resolve) {
setTimeout(resolve, duration);
});
}
timeout(1000).then(function() {
throw 'worky!';
// return Promise.reject('worky'); also works
}).catch(function(e) {
console.log(e); // 'worky!'
});
もう一つの重要な事実は、reject()
はしない return
ステートメントのように制御フローを終了させることです。対照的にthrow
は制御フローを終了させます。
例:
new Promise((resolve, reject) => {
throw "err";
console.log("NEVER REACHED");
})
.then(() => console.log("RESOLVED"))
.catch(() => console.log("REJECTED"));
vs
new Promise((resolve, reject) => {
reject(); // resolve() behaves similarly
console.log("ALWAYS REACHED"); // "REJECTED" will print AFTER this
})
.then(() => console.log("RESOLVED"))
.catch(() => console.log("REJECTED"));
はい、最大の違いは、 reject はpromiseが拒否された後に実行されるコールバック関数ですが、 throw は非同期には使用できないことです。拒否を使用することを選択した場合、コードは通常の非同期方式で実行されますが、 throw はリゾルバー機能の完了を優先します(この機能は即時に実行されます)。
私が私のために問題を明確にするのを助けたのを見た私が見た例はあなたがrejectでタイムアウト機能を設定することができるということでした、例えば:
new Promise(_, reject) {
setTimeout(reject, 3000);
});
上記ではthrowで書くことは不可能でしょう。
あなたの小さな例では見分けがつかない違いが、より複雑な非同期の概念を扱うとき、両者の違いは劇的になる可能性があります。
TLDR: 関数が約束を返したり例外を投げたりするときには使いにくい。非同期関数を書くとき、拒絶された約束を返すことによって失敗を知らせることを好む
あなたの特定の例はそれらの間のいくつかの重要な違いを難読化します:
inside promiseチェーンの中でエラー処理をしているので、投げられた例外は自動的に 変換された を受け取ってpromiseを拒否します。これは、なぜそれらが互換性があるように見えるかを説明するかもしれません - そうではありません。
以下の状況を検討してください。
checkCredentials = () => {
let idToken = localStorage.getItem('some token');
if ( idToken ) {
return fetch(`https://someValidateEndpoint`, {
headers: {
Authorization: `Bearer ${idToken}`
}
})
} else {
throw new Error('No Token Found In Local Storage')
}
}
非同期と同期エラーの両方のケースをサポートする必要があるため、これはアンチパターンです。次のようになります。
try {
function onFulfilled() { ... do the rest of your logic }
function onRejected() { // handle async failure - like network timeout }
checkCredentials(x).then(onFulfilled, onRejected);
} catch (e) {
// Error('No Token Found In Local Storage')
// handle synchronous failure
}
良くない、そしてここがまさにPromise.reject
(グローバルスコープで利用可能)が助けになるところであり、事実上それ自身をthrow
から区別します。リファクタリングは次のようになります。
checkCredentials = () => {
let idToken = localStorage.getItem('some_token');
if (!idToken) {
return Promise.reject('No Token Found In Local Storage')
}
return fetch(`https://someValidateEndpoint`, {
headers: {
Authorization: `Bearer ${idToken}`
}
})
}
これで、今度はネットワーク障害に対して1つのcatch()
と トークンの欠如のための同期エラーチェックを使うことができます。
checkCredentials()
.catch((error) => if ( error == 'No Token' ) {
// do no token modal
} else if ( error === 400 ) {
// do not authorized modal. etc.
}
試してみる例です。 throwではなくrejectを使用するには、isVersionThrowをfalseに変更するだけです。
const isVersionThrow = true
class TestClass {
async testFunction () {
if (isVersionThrow) {
console.log('Throw version')
throw new Error('Fail!')
} else {
console.log('Reject version')
return new Promise((resolve, reject) => {
reject(new Error('Fail!'))
})
}
}
}
const test = async () => {
const test = new TestClass()
try {
var response = await test.testFunction()
return response
} catch (error) {
console.log('ERROR RETURNED')
throw error
}
}
test()
.then(result => {
console.log('result: ' + result)
})
.catch(error => {
console.log('error: ' + error)
})