web-dev-qa-db-ja.com

JavaScriptの約束 - 拒否と投げ

私はこの主題に関するいくつかの記事を読みましたが、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を使うことです。なぜならそれはもっと短いからですが、一方にもう一方よりも利点があるのか​​どうか疑問に思いました。

264
Naresh

一方と他方を使用する利点はありませんが、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!'
});
225
Kevin B

もう一つの重要な事実は、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"));
158
lukyer

はい、最大の違いは、 reject はpromiseが拒否された後に実行されるコールバック関数ですが、 throw は非同期には使用できないことです。拒否を使用することを選択した場合、コードは通常の非同期方式で実行されますが、 throw はリゾルバー機能の完了を優先します(この機能は即時に実行されます)。

私が私のために問題を明確にするのを助けたのを見た私が見た例はあなたがrejectでタイムアウト機能を設定することができるということでした、例えば:

new Promise(_, reject) {
 setTimeout(reject, 3000);
});

上記ではthrowで書くことは不可能でしょう。

あなたの小さな例では見分けがつかない違いが、より複雑な非同期の概念を扱うとき、両者の違いは劇的になる可能性があります。

42
Blondie

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.
      }
36
maxwell

試してみる例です。 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)
})
6
Chris Livdahl