Skip to content

Rejected dependency resolution in a state hierarchy #2321

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
bhamon opened this issue Oct 21, 2015 · 7 comments
Closed

Rejected dependency resolution in a state hierarchy #2321

bhamon opened this issue Oct 21, 2015 · 7 comments

Comments

@bhamon
Copy link

bhamon commented Oct 21, 2015

I currently works on an app where a state sub-branch requires a request to be performed before everything else. If that request fails, the branch children shouldn't be accessed, especially the dependencies to be resolved.
For that purpose, I've appended a resolve dependency that performs the request on the root part of the branch (path.to.subroot for all the path.to.subroot.* children).

The bug : if the request fails, all the intermediary children resolve dependencies are executed without consideration.

Here is a Plunkr to illustrate this bug. If you click on the link, you can see in the console the resolution of foo, it's rejection, the error caught via the $stateChangeError hook, but also the second dependency (bar) that shouldn't be resolved.

@nateabele
Copy link
Contributor

This isn't a bug. Resolves are loaded optimistically. If you want one to depend on another, inject the dependency.

@bhamon
Copy link
Author

bhamon commented Oct 21, 2015

Thanks, I haven't thought to use injection to solve my issue...
It works like a charm now.
For historical purpose, here is a Plunkr with the @nateabele suggestion.

@bhamon bhamon closed this as completed Oct 21, 2015
@nateabele
Copy link
Contributor

In the next major version, resolves will be able to have different policies you can configure to enable this.

@bhamon
Copy link
Author

bhamon commented Oct 22, 2015

Just one more thought about that issue : the choice that have been made to load the resolves optmistically seems a bit akward to me. I think that the typical use-case would be to abort resolution on the first error, because there is a high probability that the resolved object is vital for the dependent views.

For now, to emulate this behaviour, we have to inject the required dependencies in every single child state's resolves, which is a pain for what I consider a "typical" use.

In contrary, if the first rejected resolve interrupts the resolution chain, people who want to ignore the error just have to append a .catch to the promise, and then they even can provide a fallback. Example :

//...
.state('test', {
  resolve:{
    foo:function() {
      return getFoo();
    },
    optimisticBar:function() {
      getBar()
      .catch(function() {
        // We can provide a bar replacement here, if needed.
        return {error:1};
      });
    }
  }
)
.state('test.child', {
  resolve:{
    childResolve:function() {
      // Only called if the previous resolves succeed.
      // Thus we have access to the resolved foo, and the optimisticBar or its fallback.
    }
  }
)

I think it makes more sense like that, but it's only my opinion ;)

@bhamon bhamon reopened this Oct 22, 2015
@nateabele
Copy link
Contributor

Just one more thought about that issue : the choice that have been made to load the resolves optmistically seems a bit awkward to me.

Things are parallelized as much as possible, because loading everything serially would be hugely inefficient.

I think that the typical use-case would be to abort resolution on the first error

Yup, except that promises have no generic cancellation mechanism. This is a known design issue and is being tracked in #2126.

@bhamon
Copy link
Author

bhamon commented Oct 23, 2015

Things are parallelized as much as possible, because loading everything serially would be hugely inefficient.

I agree on that point, my initial idea was more to parallelize on a per-branch basis and to serialize between branches :

.state('test', {
  resolve:{
    foo1:...
    foo2:...
  }
})
.state('test.child': {
  resolve:{
    bar1:...
    bar2:...
}).

// Behind the scene :
return $q.all([foo1, foo2]).then(function() {
  return $q.all([bar1, bar2]);
});

I think that the typical use-case would be to abort resolution on the first error

Yup, except that promises have no generic cancellation mechanism. This is a known design issue and is being tracked in #2126.

Agree too, there is no such mecanism. By "abort resolution", I meant "let the parent promise failure natively take care of the abortion". A simple $q.reject() for the resolution of foo1 in my "behind the scene" example would be enough to not trigger the bar* resolutions.

@nateabele
Copy link
Contributor

Agree too, there is no such mecanism. By "abort resolution", I meant "let the parent promise failure natively take care of the abortion".

Again, (unless I'm misunderstanding you here) that would mean waiting until parent promises have resolved before resolving children, which is not an acceptable default behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants