Menggunakan sukses / kesalahan / akhirnya / menangkap dengan Janji di AngularJS

112

Saya menggunakan $httpdi AngularJs, dan saya tidak yakin tentang cara menggunakan janji yang dikembalikan dan menangani kesalahan.

Saya memiliki kode ini:

$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
    });

Apakah ini cara yang baik untuk melakukannya, atau ada cara yang lebih mudah?

Joel
sumber

Jawaban:

103

Janji adalah abstraksi atas pernyataan yang memungkinkan kita mengekspresikan diri secara sinkron dengan kode asinkron. Mereka mewakili pelaksanaan tugas satu kali.

Mereka juga menyediakan penanganan pengecualian, seperti kode normal, Anda dapat mengembalikan dari janji atau melempar.

Yang Anda inginkan dalam kode sinkron adalah:

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

Versi yang dijanjikan sangat mirip:

$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.
});
Benjamin Gruenbaum
sumber
4
Bagaimana Anda akan menggunakan success(), error()dan finally()dikombinasikan dengan catch()? Atau apakah saya harus menggunakanthen(successFunction, errorFunction).catch(exceotionHandling).then(cleanUp);
Joel
3
@Joel umumnya, Anda tidak ingin pernah menggunakan successdan error(lebih suka .thendan .catchsebaliknya, Anda dapat (dan harus) menghilangkan errorFunctiondari .thenpenggunaan ac catchseperti dalam kode saya di atas).
Benjamin Gruenbaum
@BenjaminGruenbaum bisakah Anda menjelaskan mengapa Anda menyarankan untuk menghindari success/ error? Juga Eclipse saya mengamuk ketika melihat .catch(, jadi saya gunakan ["catch"](untuk saat ini. Bagaimana cara menjinakkan Eclipse?
Giszmo
Implementasi modul $ http Angular dari pustaka $ q menggunakan .success dan .error alih-alih .then dan .catch. Namun dalam pengujian saya, saya bisa mengakses semua properti dari $ http janji saat menggunakan janji .then dan .catch. Lihat juga jawaban zd333.
Steve K
3
@SirBenBenji $ q tidak memiliki .successdan .error, $ http mengembalikan q janji $ dengan penambahan dari successdan errorpenangan - Namun, penangan ini tidak rantai dan umumnya harus dihindari jika / bila memungkinkan. Secara umum - jika Anda memiliki pertanyaan, sebaiknya tanyakan sebagai pertanyaan baru dan bukan sebagai komentar pada pertanyaan lama.
Benjamin Gruenbaum
43

Lupakan tentang penggunaan successdan errormetode.

Kedua metode tersebut tidak digunakan lagi di sudut 1.4. Pada dasarnya, alasan di balik penghentian ini adalah karena mereka tidak ramah rantai , bisa dikatakan.

Dengan contoh berikut, saya akan mencoba menunjukkan apa yang saya maksud successdan errortidak ramah rantai . Misalkan kita memanggil API yang mengembalikan objek pengguna dengan alamat:

Objek pengguna:

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

Panggilan ke 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'}
    });
};

Apa yang terjadi?

Karena successdan errormengembalikan janji asli , yaitu yang dikembalikan oleh $http.get, objek yang diteruskan ke callback thenadalah seluruh objek pengguna , artinya input yang sama ke successcallback sebelumnya .

Jika kami merantai dua then, ini tidak akan terlalu membingungkan:

$http.get('/user')
    .then(function (user) {
        return user.address;  
    })
    .then(function (obj) {  
        console.log(obj); // -> 'San Francisco'
    });
};
Michael P. Bazos
sumber
1
Juga diperhatikan bahwa successdan erroryang hanya ditambahkan ke segera kembali dari $httppanggilan (tidak prototipe), jadi jika Anda memanggil metode janji lain di antara mereka (seperti, Anda biasanya sebut return $http.get(url)dibungkus perpustakaan dasar, tetapi kemudian memutuskan untuk beralih pemintal di panggilan perpustakaan dengan return $http.get(url).finally(...)) maka Anda tidak akan lagi memiliki metode kenyamanan tersebut.
drzaus
35

Saya pikir jawaban sebelumnya benar, tetapi berikut adalah contoh lain (hanya fyi, success () dan error () tidak digunakan lagi menurut halaman Utama AngularJS :

$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');
    });
grepit
sumber
3
Akhirnya tidak membalas tanggapan, setahu saya.
diplosaurus
11

Jenis perincian apa yang Anda cari? Anda biasanya bisa bertahan dengan:

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

Saya telah menemukan bahwa "akhirnya" dan "menangkap" lebih baik saat merangkai banyak janji.

justin
sumber
1
Dalam contoh Anda, penangan kesalahan hanya menangani kesalahan $ http.
Benjamin Gruenbaum
1
Ya, saya masih perlu menangani pengecualian dalam fungsi berhasil / kesalahan juga. Dan kemudian saya membutuhkan semacam penangan umum (di mana saya dapat mengatur hal-hal seperti loading = false)
Joel
1
Anda memiliki tanda kurung, bukan tanda kurung, yang menutup panggilan then () Anda.
Paul McClean
1
Ini tidak berfungsi pada kesalahan respons 404, hanya berfungsi pada .catch()Metode
elporfirio
Ini adalah jawaban yang benar untuk menangani kesalahan http yang dikembalikan ke pengontrol
Leon
5

Dalam kasus Angular $ http, fungsi success () dan error () akan membuka bungkus objek respons, sehingga tanda tangan panggilan balik akan menjadi seperti $ http (...). Success (fungsi (data, status, header, config))

untuk then (), Anda mungkin akan berurusan dengan objek respons mentah. seperti yang diposting di dokumen API AngularJS $ http

$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 terakhir (...) tidak akan diperlukan kecuali ada kesalahan baru yang muncul di rantai janji sebelumnya.

zd333
sumber
2
Metode Berhasil / Kesalahan tidak lagi digunakan.
OverMars
-3

Saya melakukannya seperti yang disarankan Bradley Braithwaite di blognya :

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;
            });
    }])

Poin Utama:

  • Fungsi penyelesaian terhubung ke fungsi. Lalu di pengontrol kami yaitu semuanya baik-baik saja, jadi kami dapat menepati janji kami dan menyelesaikannya.

  • Fungsi penolakan menautkan ke fungsi .catch di pengontrol kami yaitu ada yang tidak beres, jadi kami tidak bisa menepati janji kami dan harus menolaknya.

Ini cukup stabil dan aman dan jika Anda memiliki kondisi lain untuk menolak janji, Anda selalu dapat memfilter data Anda dalam fungsi sukses dan memanggil deferred.reject(anotherReason) dengan alasan penolakan.

Seperti yang disarankan Ryan Vice di komentar , ini mungkin tidak terlihat berguna kecuali Anda sedikit mengutak-atik responsnya.

Karena successdan errortidak digunakan lagi sejak 1.4 mungkin lebih baik menggunakan metode janji reguler thendan catchdan mengubah respons di dalam metode tersebut dan mengembalikan promise dari respons yang diubah itu.

Saya menunjukkan contoh yang sama dengan kedua pendekatan dan pendekatan ketiga di antara:

successdan errorpendekatan ( successdan errormengembalikan janji respons HTTP, jadi kami membutuhkan bantuan $quntuk mengembalikan janji data):

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;
};

thendan catchpendekatan (ini sedikit lebih sulit untuk diuji, karena lemparan):

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;
}

Namun ada solusi setengah jalan (dengan cara ini Anda dapat menghindari throwdan bagaimanapun Anda mungkin perlu menggunakan $quntuk mengejek perilaku janji dalam pengujian Anda):

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;
}

Segala jenis komentar atau koreksi dipersilakan.

Tukang arloji
sumber
2
Mengapa Anda menggunakan $ q untuk membungkus janji dalam sebuah janji. Mengapa tidak mengembalikan janji yang dikembalikan oleh $ http.get () saja?
Ryan Vice
Karena success()dan error()tidak akan membalas janji baru seperti then()halnya. Dengan $qkami membuat pabrik kami mengembalikan janji data, bukan janji respons HTTP.
Pembuat jam
tanggapan Anda membingungkan saya jadi mungkin saya tidak menjelaskan diri saya dengan baik. kecuali jika Anda memanipulasi respons, maka Anda cukup mengembalikan janji bahwa $ http mengembalikan. lihat contoh ini saya baru saja menulis: jsbin.com/belagan/edit?html,js,output
Ryan Vice
1
Saya tidak melihat nilainya. Rasanya tidak perlu bagi saya dan saya menolak ulasan kode pada proyek saya yang menggunakan pendekatan ini tetapi jika Anda mendapatkan nilai darinya maka Anda harus menggunakannya. Saya juga telah melihat beberapa janji di artikel praktik terbaik bersudut yang menyebutkan pembungkus yang tidak perlu sebagai bau.
Ryan Vice