AngularJSのPromiseで成功/エラー/最終的に/キャッチを使用する


112

私は$httpAngularJsで使用していますが、返されたpromiseの使用方法とエラーの処理方法がわかりません。

私はこのコードを持っています:

$http
    .get(url)
    .success(function(data) {
        // Handle data
    })
    .error(function(data, status) {
        // Handle HTTP error
    })
    .finally(function() {
        // Execute logic independent of success/error
    })
    .catch(function(error) {
        // Catch and handle exceptions from success/error/finally functions
    });

これは良い方法ですか、それとももっと簡単な方法がありますか?

回答:


103

Promiseは、非同期コードと同期して自分自身を表現できるようにするステートメントの抽象化です。これらは、1回限りのタスクの実行を表します。

また、通常のコードと同じように、例外処理を提供し、Promiseから戻ったり、スローしたりできます。

同期コードに必要なものは次のとおりです。

try{
  try{
      var res = $http.getSync("url");
      res = someProcessingOf(res);
  } catch (e) {
      console.log("Got an error!",e);
      throw e; // rethrow to not marked as handled
  }
  // do more stuff with res
} catch (e){
     // handle errors in processing or in error.
}

約束されたバージョンは非常に似ています:

$http.get("url").
then(someProcessingOf).
catch(function(e){
   console.log("got an error in initial processing",e);
   throw e; // rethrow to not marked as handled, 
            // in $q it's better to `return $q.reject(e)` here
}).then(function(res){
    // do more stuff
}).catch(function(e){
    // handle errors in processing or in error.
});

4
どのように使用することになりsuccess()error()そしてfinally()と組み合わせますかcatch()?または、使用する必要がありますかthen(successFunction, errorFunction).catch(exceotionHandling).then(cleanUp);
Joel

3
一般的に、あなたは今まで使用したくない@Joel successerror(好む.then.catch代わりに、あなたは(とすべきである)を省略することができますerrorFunctionから.then、使用交流catch上記の私のコードのように)。
Benjamin Gruenbaum 2014年

@BenjaminGruenbaumは、なぜsuccess/ を避けるように提案するのか詳しく説明してもらえますerrorか?また、私のEclipseはを検出すると正常に動作し.catch(ないため、ここでは使用["catch"](します。どうすればEclipseを飼いならすことができますか?
Giszmo 2015年

Angularの$ qライブラリの$ httpモジュール実装は、.thenと.catchの代わりに.successと.errorを使用します。しかし、私のテストでは、.thenおよび.catchプロミスを使用するときに、$ httpプロミスのすべてのプロパティにアクセスできました。zd333の回答も参照してください。
Steve K

3
@SirBenBenji $ qは持っていない.successと、.error$ HTTPは$ qを約束返し、追加したsuccesserrorハンドラを-しかし、これらのハンドラは、チェーンをしませんし、場合/可能な場合、一般的に避けるべきです。一般に、質問がある場合は、古い質問に対するコメントとしてではなく、新しい質問として質問することをお勧めします。
Benjamin

43

successerrorメソッドの使用を忘れます。

どちらの方法もangular 1.4では廃止予定です。基本的に、非推奨の背後にある理由は、いわばチェーン可能ではありません。

次の例では、私が何を意味しsuccess連鎖可能errorではないことを実証しようと試みます。アドレスを持つユーザーオブジェクトを返すAPIを呼び出すとします。

ユーザーオブジェクト:

{name: 'Igor', address: 'San Francisco'}

APIの呼び出し:

$http.get('/user')
    .success(function (user) {
        return user.address;   <---  
    })                            |  // you might expect that 'obj' is equal to the
    .then(function (obj) {   ------  // address of the user, but it is NOT

        console.log(obj); // -> {name: 'Igor', address: 'San Francisco'}
    });
};

どうした?

元のpromise、つまりによって返されるpromisesuccesserror返すため、のコールバックに渡されるオブジェクトはユーザーオブジェクト全体です。つまり、前述の同じ入力です。$http.getthensuccessコールバックです。

2つを連鎖させていた場合then、これは混乱が少ないでしょう。

$http.get('/user')
    .then(function (user) {
        return user.address;  
    })
    .then(function (obj) {  
        console.log(obj); // -> 'San Francisco'
    });
};

1
また、価値があることを指摘successし、をerrorしているだけの即時復帰に追加$httpあなたがそれらの間に別の約束メソッドを呼び出す場合はそう(のような、あなたが正常に呼び出し、コール(ないプロトタイプ)return $http.get(url)ベースライブラリに包まれたが、後にスピナーを切り替えることを決定ライブラリをreturn $http.get(url).finally(...))で呼び出すと、それらの便利なメソッドはなくなります。
drzaus

35

以前の答えは正しいと思いますが、ここに別の例があります(AngularJS メインページによると、fyi、success()、error()は非推奨です)。

$http
    .get('http://someendpoint/maybe/returns/JSON')
    .then(function(response) {
        return response.data;
    }).catch(function(e) {
        console.log('Error: ', e);
        throw e;
    }).finally(function() {
        console.log('This finally block');
    });

3
最後に、私の知る限り、応答を返しません。
ディプロサウルス

11

どのタイプの粒度をお探しですか?通常、次の方法で対処できます。

$http.get(url).then(
  //success function
  function(results) {
    //do something w/results.data
  },
  //error function
  function(err) {
    //handle error
  }
);

「finally」と「catch」は、複数のpromiseをチェーンするときに適しています。


1
あなたの例では、エラーハンドラーは$ httpエラーのみを処理します。
Benjamin Gruenbaum 2014年

1
はい、成功/エラー関数の例外も処理する必要があります。そして、私はある種の一般的なハンドラが必要です(ここでのようなものを設定できますloading = false
Joel

1
then()呼び出しを終了する括弧の代わりに中括弧があります。
Paul McClean、2015

1
これは404応答エラーでは機能せず、.catch()メソッドでのみ機能します
elporfirio

これは、コントローラーに返されたhttpエラーを処理するための正しい答えです
Leon

5

Angular $ httpの場合、success()およびerror()関数は応答オブジェクトがアンラップされるため、コールバックシグネチャは$ http(...)。success(function(data、status、headers、config))のようになります。

then()の場合、おそらく生の応答オブジェクトを処理します。AngularJS $ http APIドキュメントに投稿されたものなど

$http({
        url: $scope.url,
        method: $scope.method,
        cache: $templateCache
    })
    .success(function(data, status) {
        $scope.status = status;
        $scope.data = data;
    })
    .error(function(data, status) {
        $scope.data = data || 'Request failed';
        $scope.status = status;
    });

最後の.catch(...)は、以前のpromiseチェーンで新しいエラーがスローされない限り必要ありません。


2
Success / Errorメソッドは廃止されました。
OverMars 2018

-3

Bradley Braithwaiteが彼のブログで提案しているように私はそれをしています:

app
    .factory('searchService', ['$q', '$http', function($q, $http) {
        var service = {};

        service.search = function search(query) {
            // We make use of Angular's $q library to create the deferred instance
            var deferred = $q.defer();

            $http
                .get('http://localhost/v1?=q' + query)
                .success(function(data) {
                    // The promise is resolved once the HTTP call is successful.
                    deferred.resolve(data);
                })
                .error(function(reason) {
                    // The promise is rejected if there is an error with the HTTP call.
                    deferred.reject(reason);
                });

            // The promise is returned to the caller
            return deferred.promise;
        };

        return service;
    }])
    .controller('SearchController', ['$scope', 'searchService', function($scope, searchService) {
        // The search service returns a promise API
        searchService
            .search($scope.query)
            .then(function(data) {
                // This is set when the promise is resolved.
                $scope.results = data;
            })
            .catch(function(reason) {
                // This is set in the event of an error.
                $scope.error = 'There has been an error: ' + reason;
            });
    }])

キーポイント:

  • resolve関数は、コントローラーの.then関数にリンクします。つまり、すべて正常なので、約束を守って解決できます。

  • reject関数は、コントローラーの.catch関数にリンクしています。つまり、何か問題が発生したため、約束を守ることができず、拒否する必要があります。

それは非常に安定していて安全であり、約束を拒否する他の条件がある場合は、常に成功関数でデータをフィルタリングしdeferred.reject(anotherReason)、拒否の理由で呼び出すことができます。

Ryan Viceがコメント示唆したように、いわば、応答を少しいじらない限り、これは有用とは見なされない可能性があります。

のでsuccesserror1.4以降廃止され、多分通常の約束のメソッドを使用することをお勧めしますthencatch、それらのメソッド内のレスポンスを変換し、その変換された応答の約束を返します。

私は両方のアプローチと3番目の中間のアプローチで同じ例を示しています。

successそしてerrorアプローチ(successそしてerrorHTTP応答$qのpromiseを返すため、データのpromiseを返すための助けが必要です):

function search(query) {
  // We make use of Angular's $q library to create the deferred instance
  var deferred = $q.defer();

  $http.get('http://localhost/v1?=q' + query)
  .success(function(data,status) {
    // The promise is resolved once the HTTP call is successful.
    deferred.resolve(data);              
  })

  .error(function(reason,status) {
    // The promise is rejected if there is an error with the HTTP call.
    if(reason.error){
      deferred.reject({text:reason.error, status:status});
    }else{
      //if we don't get any answers the proxy/api will probably be down
      deferred.reject({text:'whatever', status:500});
    }
  });

  // The promise is returned to the caller
  return deferred.promise;
};

thencatchアプローチ(スローのため、これはテストが少し難しい):

function search(query) {

  var promise=$http.get('http://localhost/v1?=q' + query)

  .then(function (response) {
    // The promise is resolved once the HTTP call is successful.
    return response.data;
  },function(reason) {
    // The promise is rejected if there is an error with the HTTP call.
    if(reason.statusText){
      throw reason;
    }else{
      //if we don't get any answers the proxy/api will probably be down
      throw {statusText:'Call error', status:500};
    }

  });

  return promise;
}

ただし、中途半端な解決策があります(この方法で回避できますがthrow、とにかく$qテストでpromiseの動作を模擬するために使用する必要があります)。

function search(query) {
  // We make use of Angular's $q library to create the deferred instance
  var deferred = $q.defer();

  $http.get('http://localhost/v1?=q' + query)

  .then(function (response) {
    // The promise is resolved once the HTTP call is successful.
    deferred.resolve(response.data);
  },function(reason) {
    // The promise is rejected if there is an error with the HTTP call.
    if(reason.statusText){
      deferred.reject(reason);
    }else{
      //if we don't get any answers the proxy/api will probably be down
      deferred.reject({statusText:'Call error', status:500});
    }

  });

  // The promise is returned to the caller
  return deferred.promise;
}

どんな種類のコメントや訂正も歓迎します。


2
なぜ$ qを使用してpromiseをpromiseにラップするのでしょうか。$ http.get()によって返されるpromiseを返さないのはなぜですか?
Ryan Vice

なぜならsuccess()error()そうではなく、新しい約束を返すことはありませんthen()。では$q、私たちは私たちの工場ではなく、HTTPレスポンスの約束のデータの約束を返すようにします。
時計職人2016

あなたの反応は私を混乱させるので、多分私は自分自身をうまく説明していません。応答を操作しているのでなければ、$ httpが返す約束を単純に返すことができます。私が書いたこの例を参照してください:jsbin.com/belagan/edit?html,js,output
Ryan Vice

1
値がわかりません。それは私には不必要だと感じ、このアプローチを使用する私のプロジェクトのコードレビューを拒否しますが、それからあなたの価値を得るなら、あなたはそれを使うべきです。私はまた、不必要なラッピングを匂いとして呼びかける角度付きのベストプラクティス記事でいくつかの約束を見てきました。
Ryan Vice

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.