Angular 1.6.0までは、Angularアプリで約束を解決するためのパターンがありました。
resource.get().$promise
.then(function (response) {
// do something with the response
}, function (error) {
// pass the error the the error service
return errorService.handleError(error);
});
そして、これが私たちがカルマのエラーを引き起こしている方法です:
resourceMock.get = function () {
var deferred = $q.defer();
deferred.reject(error);
return { $promise: deferred.promise };
};
現在、1.6.0へのアップデートで、Angularは私たちの単体テストで(Karmaで)拒否された約束に対して "おそらく未処理の拒否"エラーで不平を言っています。しかし、エラーサービスを呼び出す2番目の関数で拒否を処理しています。
Angularは、ここで何を探しているのですか?拒絶反応をどのように「処理」したいのでしょうか。
このコードをあなたの設定に追加してみてください。私は一度同様の問題を抱えていた、そしてこの回避策はトリックをした。
app.config(['$qProvider', function ($qProvider) {
$qProvider.errorOnUnhandledRejections(false);
}]);
ここに示すコードは、.then
への呼び出しの前に発生した拒否を処理します。そのような状況では、あなたが.then
に渡す2番目のコールバックが呼び出され、そして拒否が処理されます。
しかし、あなたが.then
を呼び出すという約束が成功すると、それは最初のコールバックを呼び出します。 このコールバックが例外をスローするか、または拒否されたプロミスを返す場合、この結果としての拒否は処理されません。これがまさに Promises/A + 仕様に準拠したpromise実装が機能し、Angular promiseが準拠していることです。
次のコードでこれを説明できます。
function handle(p) {
p.then(
() => {
// This is never caught.
throw new Error("bar");
},
(err) => {
console.log("rejected with", err);
});
}
handle(Promise.resolve(1));
// We do catch this rejection.
handle(Promise.reject(new Error("foo")));
もしもあなたがこれをPromises/A +にも準拠しているNodeで実行すれば、
rejected with Error: foo
at Object.<anonymous> (/tmp/t10/test.js:12:23)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
at bootstrap_node.js:509:3
(node:17426) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: bar
Angular 1.5.9にロールバックしてテストを再実行することで問題を発見しました。これは単純なインジェクションの問題ですが、Angular 1.6.0は代わりに "Possibly Unhandled Rejection"エラーをスローして実際のエラーを難読化することでこれを置き換えました。
最初の選択肢は、$ qProvider設定でerrorOnUnhandledRejections
を推奨されるように設定することでエラーを無効にして隠すことです。Cengkuru Michael
BUTこれはロギングをオフにするだけです。エラー自体は残ります
この場合のより良い解決方法は次のようになります。.catch(fn)
メソッドで拒否を処理する:
resource.get().$promise
.then(function (response) {})
.catch(function (err) {});
リンク:
コード内に複数の場所で追加の.catch(function () {})
を入力しなくても済むようにするには、decorator
を$exceptionHandler
に追加します。
これは他のものよりも冗長なオプションですが、1箇所で変更するだけで済みます。
angular
.module('app')
.config(configDecorators);
configDecorators.$inject = ["$provide"];
function configDecorators($provide) {
$provide.decorator("$exceptionHandler", exceptionHandler);
exceptionHandler.$inject = ['$delegate', '$injector'];
function exceptionHandler($delegate, $injector) {
return function (exception, cause) {
if ((exception.toString().toLowerCase()).includes("Possibly unhandled rejection".toLowerCase())) {
console.log(exception); /* optional to log the "Possibly unhandled rejection" */
return;
}
$delegate(exception, cause);
};
}
};
ErrorOnUnhandledRejectionをオフにすることで問題を隠すことができますが、エラーには「可能性のある拒否を処理する」必要があることが示されているので、約束にキャッチを追加する必要があります。
resource.get().$promise
.then(function (response) {
// do something with the response
}).catch(function (error)) {
// pass the error to the error service
return errorService.handleError(error);
});
テスト実行中も同じ動作を観察しました。本番コードではうまく機能し、テストでのみ失敗するのは不思議です。
あなたのテストを満足させる簡単な解決策はあなたの約束のモックにcatch(angular.noop)
を加えることです。上記の例の場合、これは次のようになります。
resourceMock.get = function () {
var deferred = $q.defer();
deferred.reject(error);
return { $promise: deferred.promise.catch(angular.noop) };
};
Angular 1.6.7にアップデートした後も同じ問題に直面していましたが、コードを調べたところ、$interval.cancel(interval);
にエラーが発生しました。
angular-mocks
を最新バージョン(1.7.0)に更新すると、問題は解決しました。
それはあなたの特定の状況ではないかもしれませんが、私は同様の問題を抱えていました。
私の場合は、angular-i18nを使用し、ロケール辞書を非同期に取得していました。問題は、取得しているjsonファイルが誤ってインデントされていることです(スペースとタブが混在しています)。 GET要求は失敗しませんでした。
インデントを修正すると問題は解決しました。
私はこれと同じ通知をいくつかの変更を加えた後に表示させました。これは、angularjs $http
サービスを使用して、1つの$q
リクエストから複数のリクエストに変更したためです。
私はそれらを配列でラップしていませんでした。例えば.
$q.all(request1, request2).then(...)
のではなく
$q.all([request1, request2]).then(...)
私はこれが誰かに時間を節約するかもしれないと思います。