title: Using $resource in a promise chain (fixing deferred anti-pattern)
body: <p>I have a service with a method that gets me a list of project types using a $resource. It's working well for me, except that if I make multiple nearly simultaneous calls (from say, two directives) each will create another request instead of using the same response/$promise/data.</p>
  
  <p>I found <a href="https://stackoverflow.com/questions/27973528/angularjs-avoid-calling-same-rest-service-twice-before-response-is-received">this</a> which led me to <a href="https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it">this</a> and TL;DR, apparently it's creating a redundant $q.defer() and is actually considered to be a <a href="https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns#the-deferred-anti-pattern" rel="nofollow noreferrer">deferred anti-pattern</a>.</p>
  
  <p>The code below works well if the calls to get project types are significantly staggered (like more than milliseconds apart). The consecutive calls are resolved with the shared.projectTypes. It also works in the sense that if the request to get project types fails, the <code>dfr.reject()</code> will be triggered and be caught by <code>.catch</code> in the calling controller.</p>
  
  <pre><code>angular.module('projects')
  .factory('projectService', function(notificationService){
  
      // an object to share data gathered by this service
      var shared = {};
  
      // $resource for projects API
      var projectResource = $resource(baseApiPath + 'projects', {}, {
          ...,
          getProjectTypes: {
              method: 'GET',
              url: baseApiPath + 'projects/types'
          },
          ...
      });
  
      // loads a list of project types
      var loadProjectTypes = function(){
          var dfr = $q.defer();
  
          // if we've already done this, just return what we have.
          if(shared.projectTypes){
              dfr.resolve(shared.projectTypes);
          }
          else {
              // begin anti-pattern (?)
              projectResource.getProjectTypes(null,
              function(response){
                  shared.projectTypes = response.result.projectTypes;
                  dfr.resolve(response);
              },
              function(errResponse){
                  console.error(errResponse);
                  notificationService.setNotification('error', errResponse.data.messages[0]);
                  dfr.reject(errResponse);
              });
          }
          return dfr.promise;
      };
  
      return {
          shared: shared,
          project: projectResource,
          loadProjectTypes: loadProjectTypes
      };
  });
  </code></pre>
  
  <p>So, I read that having this extra <code>var dfr = $q.defer()</code> is not necessary as the $resource would provide all that for me. With a bit of refactoring, I ended up with this:</p>
  
  <pre><code>...
      // $resource for projects API
      var projectResource = $resource(baseApiPath + 'projects', {}, {
          ...,
          getProjectTypes: {
              method: 'GET',
              url: baseApiPath + 'projects/types',
              isArray: true,
              transformResponse: function(response){
                  return JSON.parse(response).result.projectTypes;
              }
          },
          ...
      });
  
      // loads a list of project types
      var loadProjectTypes = function(){
          return shared.projectTypes || (shared.projectTypes = projectResource.getProjectTypes());
      };
  ...
  </code></pre>
  
  <p>To clarify, I have added <code>isArray</code> and <code>transformResponse</code> to the resource because my API returns a lot of extra meta information and all I wanted was an array of types. In my <code>loadProjectTypes</code> method, I'm including the same caching we originally had, but I'm caching the result of <code>projectResource.getProjectTypes()</code> instead of the actual response data (even though that might be exactly what I'm caching because of the transformResponse).</p>
  
  <p>This works on the happy path (reduced calls to API, returns the same thing to everyone, etc) but my main problem is with the chaining and catching of errors.</p>
  
  <p>In my original anti-pattern example, if there is an error with GET /project/types, I'm using <code>dfr.reject()</code> which is then passed back to my controller where I have a .catch().</p>
  
  <p>This is code from the controller which actually makes the original request to get project types:</p>
  
  <pre><code>$q.all([
      projectService.loadProjects(),
      userService.loadUserRole('project_manager'),
      userService.loadUserRole('sales_representative'),
      projectService.loadProjectTypes(),
      clientService.loadClients()
  ])
  .then(function(response){
      // doing stuff with response
  })
  .catch(function(errResponse){
      // expecting errors from service to bubble through here
      console.error(errResponse);
  });
  </code></pre>
  
  <p>With the anti-pattern example, the <code>dfr.reject</code> is causing the error to show up here in the catch, but in my supposed non-anti-pattern example, it's not happening. I'm not sure how to reject or resolve the $resource results in the same way I was before. If one of the points of promise chaining is to have one spot to handle errors from any chain link, I was doing it right.</p>
  
  <p>I tried to use $q.resolve()/reject(), since I don't have dfr anymore, but this seems dumb and doesn't work anyway.</p>
  
  <pre><code>return shared.projectTypes || (shared.projectTypes = projectResource.getProjectTypes(null, 
      function(response){
          return $q.resolve(response);
      },
      function(errResponse){
          return $q.reject(errResponse);
      }));
  </code></pre>
  
  <p>How do I get the chain to work so that .catch() in the controller is where the errors get handled?</p>
  
  <p>Did I actually implement the anti-pattern in my original code, or was that one of the accepted ways to use $q.defer() and it wasn't an anti-pattern at all?</p>
  
  <p>In the <a href="https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it">second link I posted</a>, there is an answer that says:</p>
  
  <blockquote>
    <p>"What's wrong with it? But the pattern works! Lucky you.
    Unfortunately, it probably doesn't, as you likely forgot some edge
    case. In more than half of the occurrences I've seen, the author has
    forgotten to take care of the error handler."</p>
  </blockquote>
  
  <p>However, my original code <em>was</em> addressing the errors. It was working, except that each caller was getting it's own promise. I feel that's where I missed something.</p>
  
  <p>I might be confused, but I'm thinking that the <code>loadProjectTypes</code> method should return the same promise/data to anyone who calls it, no matter when it's called. It should be the one true source of anything projectTypes and only make the call once, the very first time.</p>
  
  <p>Any time I look for any of this (lots of purple/visited google links on these subjects), everyone is either showing chaining with contrived examples, or only using $http, or something else. I haven't found anyone doing error catching in a promise chain that uses $resource.</p>
  
  <p>UPDATE: Adding my requirements for the solution. I posted them in my answer, but wanted to include them in the original post too.</p>
  
  <p>Requirement 1: Allows multiple calls to the method, but only makes one API request which updates all callers with the same data.</p>
  
  <p>Requirement 2: Must be able to use result of method as actual data, just as the promise spec intends. <code>var myStuff = service.loadStuff()</code> should actually set <code>myStuff</code> to be "stuff".</p>
  
  <p>Requirement 3: Must allow promise chaining so that all errors in any part of the chain can be caught by a single catch at the end of the chain. As I've found in my solution, there can be more than one chain, and more than one catch, but the point is that each chain has a catch, and any "links" in the chain that break should all report their errors to their respective catch.</p>