javascript angular ui-router 中的承诺依赖解析顺序

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21983452/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 22:09:04  来源:igfitidea点击:

Promise dependency resolution order in angular ui-router

javascriptangularjsdependency-injectionpromiseangular-ui-router

提问by frapontillo

I have set up a top-level controller that is instantiated only when a promise (returned by a Configfactory) is successfully resolved. That promise basically downloads the Web app configuration, with RESTful endpoints and so on.

我已经设置了一个顶级控制器,只有在Config成功解决承诺(由工厂返回)时才会实例化。该承诺基本上下载 Web 应用程序配置,以及 RESTful 端点等。

$stateProvider
  .state('app', {
    url: '/',
    templateUrl: 'views/_index.html',
    controller: 'MainCtrl',
    resolve: {
      config: 'Config'
    }
  });

This setup allows me to kind-of assert that the configuration is properly loaded before any lower controller gets a chance to use it.

这个设置允许我在任何较低的控制器有机会使用它之前断言配置已正确加载。

Now I need to inject, in a deeper nested controller, another factorythat uses Configand only works when it is resolved (look at it like a $resourcewrapper that needs some Web service URLs). If I do:

现在我需要在更深的嵌套控制器中注入另一个factory使用Config并且仅在解析时才起作用的控制器(将其$resource视为需要一些 Web 服务 URL的包装器)。如果我做:

$stateProvider
  .state('app.bottom.page', {
    url: '/bottom/page',
    templateUrl: 'views/_a_view.html',
    controller: 'BottomLevelCtrl',
    resolve: {
      TheResource: 'MyConfigDependingResource'
    }
  });

it looks like the resolveevaluation order does not follow the controller hierarchy from top to bottom, but from bottom to top, therefore:

看起来resolve评估顺序不是从上到下遵循控制器层次结构,而是从下到上,因此:

  1. app.bottom.pageis entered
  2. ui-routerattempts to resolve MyConfigDependingResource, but the injection fails, because Confighas never been initialized
  3. The ui-routerresolution stops because of an error (without even throwing Errors, but that's another issue), and Configis never initialized by the top level controller
  1. app.bottom.page被输入
  2. ui-router尝试解析MyConfigDependingResource,但注入失败,因为Config从未被初始化
  3. ui-router决议停止,因为一个错误的(甚至没有扔ErrorS,但这是另一个问题),并且Config永远不会被顶层控制器初始化

Why is ui-routerresolving dependencies in a reverse order? How can I easily resolve my TheResourceobject afterthe top level MainCtrlhas resolved Config(without relying on $inject, of course)?

为什么ui-router以相反的顺序解析依赖项?我怎么能轻易解决我的TheResource对象的顶层MainCtrl已经解决了Config(不依靠$inject,当然)?

UPDATE: from this plnkr's logyou can see that the top level resolveis attempted only after the nested controller has started its own resolving process.

更新:从这个 plnkr 的日志中,您可以看到resolve只有在嵌套控制器开始自己的解析过程后才尝试顶级。

回答by Nikolay Melnikov

Similarly to @Kasper Lewau's answer, one may specify a dependency on resolves withing a single state. If one of your resolves depends on one or more resolve properties from the same resolve block. In my case checkSrelies on two other resolves

与@Kasper Lewau 的回答类似,可以指定对具有单个状态的解析的依赖。如果您的解析之一依赖于来自同一解析块的一个或多个解析属性。在我的情况下checkS依赖于其他两个解决方案

.state('stateofstate', {
    url: "/anrapasd",
    templateUrl: "views/anrapasd.html",
    controller: 'SteofsteCtrl',
    resolve: {
        currU: function(gamMag) {
            return gamMag.checkWifi("jabadabadu")
        },
        userC: function(gamUser, $stateParams) {
            return gamUser.getBipi("oink")
        },
        checkS: ['currU', 'userC', 'gamMag', function(currU, userC, gamMag) {
            return gamMag.check(currU, userC);
        }]
    }
})


**PS: **Check the "Resolves" section of the following documentfor more details about the inner-workings of resolve.

**PS:**检查以下文档的“解决”部分,了解有关resolve.

回答by Kasper Lewau

Resolve objects run in parallel, and as such doesn't follow a certain hierarchy. However, nested resolves are possible by defining a higher order resolve object as a dependency to your secondary resolves.

Resolve 对象并行运行,因此不遵循特定的层次结构。但是,通过将高阶解析对象定义为辅助解析的依赖项,可以实现嵌套解析。

$stateProvider
  .state('topState', {
    url: '/',
    resolve: {
      mainResolveObj: ['$someService', function ($someService) {
        return 'I am needed elsewhere!';
      }]
    }
  })
  .state('topState.someOtherState', {
    url: '/some-other-place',
    resolve: {
      someOtherResolveObj: ['mainResolveObj', function (mainResolveObj) {
        console.log(mainResolveObj); //-> 'I am needed elsewhere!'. 
      }]
    }
  });

Kind of a bad example, but I take it you get the gist of it. Define the name of a higher level resolve object as a dependency to your lower level resolve and it'll wait for it to resolve first.

一个不好的例子,但我认为你明白了它的要点。将更高级别的解析对象的名称定义为对较低级别的解析的依赖,它会先等待它解析。

This is how we've solved preloading certain data before lower order resolve objects, aswell as authentication requirements (among other things).

这就是我们如何在低阶解析对象以及身份验证要求(除其他外)之前解决预加载某些数据的方法。

Good luck.

祝你好运。

回答by Josh

I agree with you that the resolves should chain, and I've hit many problems around this area.

我同意你的观点,解决方案应该是连锁的,我在这方面遇到了很多问题。

However, they don't, so the solution I have come up with is to use my own promise stored in a service which you resolve when the data is complete. I have tried my best to edit your plunkr to work but to no avail. I am no longer hitting your errors though, so hopefully you can work from this: http://plnkr.co/edit/Yi65ciQPu7lxjkFMBKLn?p=preview

但是,他们没有,所以我想出的解决方案是使用我自己存储在服务中的承诺,您在数据完成时解析该服务。我已尽力编辑您的 plunkr 以使其正常工作,但无济于事。不过,我不再遇到您的错误,所以希望您可以从这里开始工作:http: //plnkr.co/edit/Yi65ciQPu7lxjkFMBKLn?p=preview

What it's doing:

它在做什么:

Store the config in a state alongside a new promise object

将配置存储在一个新的承诺对象旁边的状态中

myapp.service('state', function($q) {
  this.load = $q.defer();
  this.config = {}
})

On your first resolve, store your config in this service and once ready resolve the new promise we created above.

在您第一次解决时,将您的配置存储在此服务中,一旦准备好解决我们上面创建的新承诺。

myapp.factory('Config', function($http, $log, state) {
  return $http.get('example.json')
    .then(function (data) {
      angular.extend(state.config, data.data);
      state.load.resolve();
    });
});

The final and most important step is to not call our second the content of the childs resolve function until after our promise above is resolved:

最后也是最重要的一步是在上面的 promise 被解决之前,不要调用我们的第二个孩子的 resolve 函数的内容:

myapp.factory('MyConfigDependingResource', function($log, state) {
  return state.load.promise.then(function() {
    if (!state.config.text) {
      $log.error('Config is uninitialized!');
    }
    else {
      // Do wonderful things
    }
    return
  });
});

The main thing you will need to be aware of is that the config is now stored in a state. There should be ways around this but this is how I've done it before.

您需要注意的主要事情是配置现在存储在状态中。应该有办法解决这个问题,但我以前就是这样做的。

回答by Remento

While they cannot be chained, you can call one from another:

虽然它们不能被链接,但您可以从另一个调用它们:

var resolve2 = ['$stateParams',function($stateParams){
  // do resolve 2 stuff
  ...
  return true;
}];

var resolve1 = ['$stateParams',function($stateParams){
  // do resolve 1 stuff
  ...
  // now do resolve2 stuff
  return $injector.invoke(resolve2,this,{$stateParams:$stateParams});
}];


$stateProvider.state("myState", {
  resolve: {
    resolve1: resolve1
  }
});