Використання успіху / помилки / нарешті / спіймати з Promises in AngularJS


112

Я використовую $httpв AngularJs, і я не впевнений, як використовувати повернуту обіцянку та обробляти помилки.

У мене є цей код:

$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

Обіцяння - це абстракція над твердженнями, які дозволяють нам виражатись синхронно з асинхронним кодом. Вони являють собою виконання одноразового завдання.

Вони також забезпечують обробку винятків, як і звичайний код, ви можете повернутися з обіцянки або можете кинути.

Що ви хочете в синхронному коді:

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);
Джоель

3
@Joel взагалі, ви не хочете ніколи використовувати successі error(віддайте перевагу, .thenа .catchзамість цього ви можете (і повинні) пропустити використання errorFunctionвід .thenвикористання ac, catchяк у моєму коді вище).
Бенджамін Грюнбаум

@BenjaminGruenbaum Ви могли б пояснити, чому ви пропонуєте уникати success/ error? Також мій Eclipse працює amok, коли він бачить .catch(, тому я ["catch"](зараз користуюся . Як я можу приручити затемнення?
Гізмо

Реалізація $ http модуля Angular у бібліотеці $ q використовує .success і .error замість .then і .catch. Однак у своїх тестах я міг отримати доступ до всіх властивостей $ http обіцянки при використанні обіцянок .then та .catch. Також дивіться відповідь zd333.
Стів К

3
@SirBenBenji $ д не має .successі .error, $ HTTP повертає $ Q обіцянку з додаванням з successі errorобробників - однак, ці обробники НЕ ланцюг і слід уникати , якщо / коли це можливо. Взагалі - якщо у вас є питання, найкраще задавати їх як нове запитання, а не як коментар до старого.
Бенджамін Груенбаум

43

Забудьте про використання successта errorспосіб.

Обидва способи застаріли в куті 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'}
    });
};

Що трапилось?

Оскільки successі errorповернути первісну обіцянку , тобто ту, яку повернув $http.get, об'єкт, переданий зворотному виклику, thenє цілим об'єкт користувача , тобто той самий вхід до попереднього successзворотного виклику.

Якби ми прикували дві 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 застаріла, лише фій, успіх () та помилка () :

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

Я виявив, що "нарешті" та "зловити" краще, коли прив'язують кілька обіцянок.


1
У вашому прикладі обробник помилок обробляє лише помилки $ http.
Бенджамін Грюнбаум

1
Так, мені все ще потрібно обробляти винятки у функціях успіху / помилки. І тоді мені потрібен якийсь звичайний обробник (де я можу встановити такі речі loading = false)
Джоель,

1
У вас є дужка замість дужок, що закривають ваш тоді () дзвінок.
Пол МакКлін

1
Це не працює над 404 помилками відповіді, працює лише над .catch()методом
elporfirio

Це правильна відповідь на обробку http-помилок, повернених контролерам
Леон

5

У випадку Angular $ http функція success () та error () призведе до того, що об'єкт відповіді буде розгорнуто, тому підпис зворотного дзвінка буде подібний $ http (...). Success (функція (дані, статус, заголовки, конфігурація))

for then (), ви, мабуть, матимете справу з необробленим об'єктом відповіді. наприклад, розміщеного в документі 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 (...) не знадобиться, якщо в попередньому ланцюжку обіцянок не буде викинута нова помилка.


2
Методи успіху / помилки застарілі.
OverMars

-3

Я роблю це так, як пропонує Бредлі Брейтвайт у своєму блозі :

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

Ключові моменти:

  • Функція дозволу посилається на функцію .then в нашому контролері, тобто все добре, тому ми можемо виконати свою обіцянку і вирішити її.

  • Функція відхилення посилається на функцію .catch в нашому контролері, тобто щось пішло не так, тому ми не можемо дотримати обіцянку і потрібно її відхилити.

Це досить стабільно і безпечно, і якщо у вас є інші умови відхилити обіцянку, ви завжди можете відфільтрувати свої дані у функції успіху та зателефонувати deferred.reject(anotherReason)з причиною відхилення.

Як запропонував Райан Віце в коментарях , це може не вважатися корисним, якщо ви трохи не поспішаєте з відповіддю, так би мовити.

Тому що successі errorзастаріли з 1.4, можливо, краще використовувати методи звичайних обіцянок thenіcatch і перетворити відповідь в цих методах і повернути обіцянку цього трансформованого відповіді.

Я показую той самий приклад з обома підходами та третім підходом між ними:

successі errorпідхід ( successі errorповернути обіцянку відповіді HTTP, тому нам потрібна допомога, $qщоб повернути обіцянку даних):

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

thenі catchпідхід (це трохи складніше перевірити через кидок):

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щоб насмішити поведінку з обіцянками у своїх тестах):

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, щоб обернути обіцянку в обіцянку. Чому б просто не повернути обіцянку, яку повертає $ http.get ()?
Віян Райан

Тому що success()і error()не поверне нову обіцянку, як then()це робиться. З $qми робимо наш завод , щоб повернути обіцянку даних замість обіцянки відповіді HTTP.
годинниковий виробник

ти відповідь мене бентежить, тому, можливо, я не пояснюю себе добре. якщо ви не маніпулюєте відповіддю, ви можете просто повернути обіцянку, що $ http повернеться. дивіться цей приклад, який я щойно писав: jsbin.com/belagan/edit?html,js,output
Райан

1
Я не бачу значення. Мені це здається непотрібним, і я відкидаю огляди коду на мої проекти, які використовують цей підхід, але якщо ви отримуєте цінність від цього, то вам слід використовувати його. Я також бачив декілька обіцянок у кутових статтях з найкращої практики, які закликають зайве обгортання як запах.
Віян Райан

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.