Sử dụng thành công / lỗi / cuối cùng / bắt với Lời hứa trong AngularJS


112

Tôi đang sử dụng $httptrong AngularJs và tôi không chắc chắn về cách sử dụng lời hứa trả về và xử lý lỗi.

Tôi có mã này:

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

Đây có phải là một cách tốt để làm điều đó, hay là có một cách dễ dàng hơn?

Câu trả lời:


103

Lời hứa là một sự trừu tượng hóa các câu lệnh cho phép chúng ta thể hiện bản thân một cách đồng bộ với mã không đồng bộ. Chúng đại diện cho việc thực hiện một nhiệm vụ một lần.

Họ cũng cung cấp xử lý ngoại lệ, giống như mã thông thường, bạn có thể trả về từ một lời hứa hoặc bạn có thể ném.

Những gì bạn muốn trong mã đồng bộ là:

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

Phiên bản quảng cáo rất giống nhau:

$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
Làm thế nào bạn sẽ sử dụng success(), error()finally()kết hợp với catch()? Hay tôi phải sử dụngthen(successFunction, errorFunction).catch(exceotionHandling).then(cleanUp);
Joel

3
@Joel thường, bạn không muốn bao giờ sử dụng successerror(thích .then.catchthay vào đó, bạn có thể (và nên) bỏ qua các errorFunctiontừ .thensử dụng ac catchnhư trong mã của tôi ở trên).
Benjamin Gruenbaum

@BenjaminGruenbaum bạn có thể giải thích lý do tại sao bạn nên tránh success/ errorkhông? Ngoài ra, Eclipse của tôi chạy amok khi nó nhìn thấy .catch(, vì vậy tôi sử dụng ["catch"](ngay bây giờ. Làm cách nào để chế ngự Eclipse?
Giszmo

Việc triển khai mô-đun $ http của thư viện $ q của Angular sử dụng .success và .error thay vì .then và .catch. Tuy nhiên, trong các thử nghiệm của mình, tôi có thể truy cập tất cả các thuộc tính của $ http Hứa khi sử dụng .then và .catch. Cũng xem câu trả lời của zd333.
Steve K

3
@SirBenBenji $ q không có .success.error, $ http trả về một lời hứa $ q với việc bổ sung các trình xử lý successerror- tuy nhiên, các trình xử lý này không chuỗi và thường nên tránh nếu / khi có thể. Nói chung - nếu bạn có câu hỏi, tốt nhất nên hỏi họ như một câu hỏi mới chứ không phải như một nhận xét về một câu hỏi cũ.
Benjamin Gruenbaum

43

Quên về cách sử dụng successerrorphương pháp.

Cả hai phương pháp đều không được chấp nhận trong góc 1.4. Về cơ bản, lý do đằng sau sự phản đối là do chúng không thân thiện với khả năng truyền tải.

Với ví dụ sau, tôi sẽ cố gắng chứng minh ý tôi muốn nói successerrorkhông thân thiện với người có thể chữa được . Giả sử chúng ta gọi một API trả về một đối tượng người dùng có địa chỉ:

Đối tượng người dùng:

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

Gọi tới 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'}
    });
};

Chuyện gì đã xảy ra?

Bởi vì successerrortrả về lời hứa ban đầu , tức là lời hứa được trả về $http.get, đối tượng được chuyển đến lệnh gọi lại của thenlà toàn bộ đối tượng người dùng , nghĩa là cùng một đầu vào cho lệnh successgọi lại trước đó .

Nếu chúng ta đã xâu chuỗi hai then, điều này sẽ đỡ khó hiểu hơn:

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

1
Cũng đáng chú ý là successerrorđược chỉ thêm vào sự trở lại ngay lập tức các $httpcuộc gọi (không phải là nguyên mẫu), vì vậy nếu bạn gọi một phương pháp hứa hẹn giữa chúng (như, bạn thường gọi return $http.get(url)được bọc trong một thư viện cơ sở, nhưng sau đó quyết định để chuyển đổi một spinner trong thư viện gọi với return $http.get(url).finally(...)) thì bạn sẽ không còn có các phương thức tiện lợi đó nữa.
drzaus

35

Tôi nghĩ các câu trả lời trước là đúng, nhưng đây là một ví dụ khác (chỉ là fyi, thành công () và lỗi () không được chấp nhận theo Trang chính của 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');
    });

3
Cuối cùng không trả lại phản hồi, theo hiểu biết của tôi.
diplosaurus,

11

Bạn đang tìm kiếm loại chi tiết nào? Bạn thường có thể nhận được bằng:

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

Tôi thấy rằng "cuối cùng" và "bắt" tốt hơn khi xâu chuỗi nhiều lời hứa.


1
Trong ví dụ của bạn, trình xử lý lỗi chỉ xử lý các lỗi $ http.
Benjamin Gruenbaum

1
Có, tôi vẫn cần xử lý các ngoại lệ trong các hàm thành công / lỗi. Và sau đó tôi cần một số loại trình xử lý chung (nơi tôi có thể thiết lập những thứ như thế loading = false)
Joel

1
Bạn có một dấu ngoặc nhọn thay vì dấu ngoặc đơn đóng lại lệnh gọi then () của bạn.
Paul McClean

1
Doesnt làm việc này trên 404 lỗi phản ứng, chỉ hoạt động trên .catch()Method
elporfirio

Đây là câu trả lời chính xác để xử lý lỗi http trả về cho bộ điều khiển
Leon

5

Trong trường hợp Angular $ http, hàm Success () và error () sẽ có đối tượng phản hồi được bỏ gói, vì vậy chữ ký gọi lại sẽ giống như $ http (...). Thành công (hàm (dữ liệu, trạng thái, tiêu đề, cấu hình))

sau đó (), bạn có thể sẽ xử lý đối tượng phản hồi thô. chẳng hạn như được đăng trong tài liệu 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 (...) cuối cùng sẽ không cần trừ khi có lỗi mới xuất hiện trong chuỗi lời hứa trước đó.


2
Các phương pháp thành công / lỗi không được dùng nữa.
OverMars

-3

Tôi làm điều đó như Bradley Braithwaite gợi ý trong blog của anh ấy :

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

Những điểm chính:

  • Hàm giải quyết liên kết với hàm .then trong bộ điều khiển của chúng tôi, tức là tất cả đều ổn, vì vậy chúng tôi có thể giữ lời hứa và giải quyết nó.

  • Hàm từ chối liên kết với hàm .catch trong bộ điều khiển của chúng tôi, tức là đã xảy ra lỗi, vì vậy chúng tôi không thể giữ lời hứa của mình và cần phải từ chối nó.

Nó khá ổn định và an toàn và nếu bạn có các điều kiện khác để từ chối lời hứa, bạn luôn có thể lọc dữ liệu của mình trong hàm thành công và gọi deferred.reject(anotherReason)với lý do từ chối.

Như Ryan Vice đã đề xuất trong các bình luận , điều này có thể không được coi là hữu ích trừ khi bạn thử một chút với phản hồi, có thể nói.

successerrorđang bị phản đối từ 1,4 có thể nó là tốt hơn để sử dụng các phương pháp hứa hẹn thường xuyên thencatchvà chuyển đổi các phản hồi trong vòng các phương pháp đó và gửi lại lời hứa đó phản ứng chuyển đổi.

Tôi đang đưa ra cùng một ví dụ với cả hai cách tiếp cận và cách tiếp cận thứ ba ở giữa:

successerrorcách tiếp cận ( successerrortrả về một lời hứa về phản hồi HTTP, vì vậy chúng tôi cần sự trợ giúp của $qviệc trả lại lời hứa về dữ liệu):

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

thencatchcách tiếp cận (điều này khó kiểm tra hơn một chút, vì quá khó):

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

Tuy nhiên, có một giải pháp nửa chừng (bằng cách này, bạn có thể tránh throwvà dù sao thì bạn có thể sẽ cần phải sử dụng $qđể chế nhạo hành vi hứa hẹn trong các thử nghiệm của mình):

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

Mọi loại nhận xét hoặc sửa chữa đều được hoan nghênh.


2
Tại sao bạn lại sử dụng $ q để gói lời hứa trong một lời hứa. Tại sao không chỉ trả lại lời hứa được trả lại bởi $ http.get ()?
Ryan Vice,

Bởi vì success()error()sẽ không trả lại một lời hứa mới như then()vậy. Với việc $qchúng tôi làm cho nhà máy của chúng tôi trả về một lời hứa về dữ liệu thay vì một lời hứa về một phản hồi HTTP.
Thợ sửa đồng hồ

câu trả lời của bạn làm tôi khó hiểu nên có lẽ tôi không giải thích rõ về bản thân. trừ khi bạn đang thao túng phản hồi thì bạn có thể chỉ cần trả lại lời hứa rằng $ http trả về. hãy xem ví dụ này mà tôi vừa viết: jsbin.com/belagan/edit?html,js,output
Ryan Vice

1
Tôi không thấy giá trị. Tôi cảm thấy điều đó là không cần thiết và tôi từ chối đánh giá mã trên các dự án của tôi sử dụng cách tiếp cận này nhưng nếu bạn nhận được giá trị từ nó thì bạn nên sử dụng nó. Tôi cũng đã thấy một vài lời hứa trong các bài báo thực hành tốt nhất góc cạnh gọi việc gói không cần thiết như một mùi.
Ryan Vice,

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.