Цепочка Angular $http звонит правильно?

Я читал около $q и promises в течение нескольких дней, и я, кажется, это понимаю... несколько. На практике я имею следующую ситуацию:

  • Выполняется запрос $http и проверяется, может ли быть выполнен следующий вызов.
  • Если первый вызов завершился неудачно, верните "нет данных" , если он преуспеет и говорит, что вызов может быть выполнен, второй вызов выполняется, если нет - "нет данных" снова. Если второй вызов завершается успешно, он возвращает данные, если нет - "нет данных" . Похоже на это (примерно, я упрощен для общей идеи, поэтому не беспокойтесь о незначительных ошибках здесь):

            return $http.get (something)
                .then(function(allowedAccess){
                    if(allowedAccess){
                        return $http.get (somethingElse)
                            .then( function(result){return {data:result} },
                            function(error){return {data:"n0pe"} }
                        )
                    } else {
                        return {data:"n0pe"}
                    }
                },
                function(){ return {data:"n0pe"} });
    

Мне сказали использовать здесь $q. Я действительно не понимаю, как и почему. $Http-вызовы уже promises.

Если есть способ сделать это чище, я его не вижу. Просто перечитал этот пост по теме. По сути, я что-то упустил/есть лучший способ сделать это?

Изменить: также просто перечитать учебник по цепочке promises - он вообще не обрабатывает сбои вызовов. В основном публиковать это как должную осмотрительность.

Изменить 2: Это более подробно из теории, о которой я спрашиваю, выдержки из первой статьи:

Это простой пример. Это становится действительно мощным, если ваш обратный вызов then() возвращает другое обещание. В этом случае следующий then() будет выполняться только после того, как обещает решение. Этот шаблон может использоваться для последовательных HTTP-запросов, например (где запрос зависит от результата предыдущего):

Это, похоже, говорит о таких цепочках:

   asyncFn1(1)
    .then(function(data){return asyncFn2(data)})
    .then(function(data){return asyncFn3(data)})

Итак, если я правильно понимаю это а). Не относится ко мне, потому что у меня нет третьей функции. б). Будет применяться ко мне, если бы у меня было три функции, потому что, когда я запускаю оператор if в первом запросе $http, и только внутри оператора if я возвращаю другое обещание. Итак, теоретически, если бы у меня было три функции асинхронного программирования, мне нужно было бы включить выражение if if в обещание?

Ответы

Ответ 1

Promises действительно помогает с составлением кода для создания асинхронных вызовов. Другими словами, они позволяют составить ваш код аналогичным образом, как вы создадите синхронный набор вызовов (с использованием цепочки .then s), и как если бы код синхронизации был в try/catch block (с .catch).

Итак, представьте, что ваши HTTP-вызовы блокировались - логика у вас будет выглядеть так:

var allowedAccess, data;
try {
  allowedAccess = $http.get(something);

  if (allowedAccess){
    try{
      var result = $http.get(somethingElse);
      data = {data: result};
    } catch (){
      data = {data: "n0pe"};
    }
  } else {
    data = {data: "n0pe"};
  }
} catch (){
  data = {data: "n0pe"};
}
return data;

Вы можете немного упростить его:

var allowedAccess, result;
try {
  allowedAccess = $http.get(something);
  var result;
  if (allowedAccess) {
     result = $http.get(somethingElse);
  } else {
     throw;
  }
  data = {data: result};
} catch () {
   data = {data: "n0pe"};
}
return data;

И это переведёт в асинхронную версию:

return $http
          .get(something)
          .then(function(allowedAccess){
             if (allowedAccess){
               return $http.get(somethingElse);
             } else {
               return $q.reject(); // this is the "throw;" from above
             }
          })
          .then(function(result){
             return {data: result};
          })
          .catch(function(){
             return {data: "n0pe"};
          })

По крайней мере, это аргументы, которые вы могли бы применить при составлении кода с помощью ветвей и асинхронных вызовов.

Я не говорю, что представленная версия оптимальна или короче - она ​​ , однако более DRY из-за одной обработки ошибок. Но просто поймите, что когда вы делаете .then(success, error), это эквивалентно try/catch по предыдущей операции async - это может потребоваться или не понадобиться в зависимости от ваших конкретных обстоятельств.

Ответ 2

Вот как я бы назвал такую ​​проблему:

// returns a promise that resolves some endpoint if allowed
function getDataWithAccess(allowed){
    return allowed ? $http.get(someEndpoint) : $q.reject();
}

// do something with data
function handleData(data){
    // do stuff with your data
}

// main chain
$http.get(accessCredEndpoint)
    .then(getDataWithAccess)
    .then(handleData)
    .catch(function(err){
        return { data: "n0pe" };
    });

Да, это очень похоже на ответ "Новый разработчик", однако я хотел сделать вывод об извлечении функций в свои собственные блоки. Это делает код более понятным.

Ответ 3

$q поможет уменьшить пирамиду вызовов следующим образом:

async-call1.then(...
  aysnc-call2.then(...

Это сообщение в блоге - http://chariotsolutions.com/blog/post/angularjs-corner-using-promises-q-handle-asynchronous-calls/ - предлагает чистый способ создания нескольких HTTP-запросов. Обратите внимание на более чистый подход с использованием $q. В случае, если вы ударили по одной конечной точке HTTP, использование вашего метода было бы просто прекрасным. Я бы сказал, что у вас все хорошо; $q может обеспечить большую гибкость в будущем.

Сообщение в блоге описывает службу при использовании $q, а код выглядит более чистым.

service('asyncService', function($http, $q) {
  return {
    loadDataFromUrls: function(urls) {
      var deferred = $q.defer();
      var urlCalls = [];
      angular.forEach(urls, function(url) {
        urlCalls.push($http.get(url.url));
      });
      // they may, in fact, all be done, but this
      // executes the callbacks in then, once they are
      // completely finished.
      $q.all(urlCalls)
      .then(...

Я тоже начинаю с promises, поэтому возьмите это с солью.