Angular.js: Support asynchronous module.run()

Created on 13 Sep 2013  ·  62Comments  ·  Source: angular/angular.js

We want to issue a few $http calls in the module run block, ideally, the run should finish only after the $http and processing finishes before the dom compiling happens.

Lots of comments won't fix feature

Most helpful comment

jfcfxekzl5m
Simple feature but too hard to implement. 😕

All 62 comments

This is not so easy but should be looked into.

The way Jasmine does this is by setting a flag, that will let the function run and wait for the callback until the flag is set to true, or until a timeout is fired.

Basically something like this:

var flag = false;
$http(...).success(flag = true);
$timeout(function () {
    flag = true;
    // Or add some error handling stuff here
}, 5000);
while (!flag) {};

It's not pretty, but should do the job. The code above isn't tested, so you may need to modify it a bit. It's getting late here. Hopefully something to get you by until something a bit prettier comes along.

+1

On a second though, this does lead to the inability to draw fancy loading screen using angular directives...

+1

Doesn't $routeProvider with resolve solve this? Maybe I didn't understand the problem...

This is more for async module init support. With that said, I no longer need this in my current projects.

@shahata $routeprovider does this, but not every use case for promise-giving runs uses routes. In my use case, it's for a module that wraps a directive and has nothing to do with routes.

+1

check if the run function returns a promise, then only procceed when it's resolved. since runBlocks is an array of functions, it shouldn't be hard at all

+1

+1 embarrassing that this got ignored

any idea when this is going to land? so frustrating to have to move initialization stuff to resolve in the router, mainly because sometimes the initialization code is url agnostic (aka, must be initialized for any type of URL that the user arrives at your SPA)

i don't think angular will be receiving any enhancements henceforth. they're all being added to angular 2.0

how long the half-assed run method will remain synchronous? it's fine and understandable that config doesn't support asynchronous calls, since you don't have any providers that do $http calls, but run does. look the type of hacks that the lack of this feature (that should've been in since the beginning) causes
https://github.com/philippd/angular-deferred-bootstrap/blob/master/src/deferred-bootstrap.js

it's embarrassing

+1

@btford is looking at how the use cases around this could be supported via the new router rather than inside run blocks.

+1

+1

+1

+1

The main problem we have is the $q is created inside a module that is being loaded when the runBlocks are being run. This means that we would need to somehow extract "some" of the services (or maybe modules) and create those first and then have a second pass that loads the rest of the modules (and services, run blocks, etc) after $q has been created.

@lgalfaso - perhaps you could look into this a bit more as part of your $injector work later this week (or next)?

I know it's just a workaround, but I've normally solved the issue of having some async bootstrapping logic by using angular ui router and having a root abstract state in combination with resolve. The root abstract state has to be always resolved first, so it's a good place to load settings, display loading animations, etc.

This would be really great.
:+1:
Waiting for this fix. Hope it will get in 1.4

+1

I got another use case: I need to init my simulated endpoints with a $http call on startup (to cache some data)...

+1

+1

just stumbled into this too...

@petebacondarwin i think it would be ok to continue loading all other modules , but defer initiating the initial navigation.

alternatively the $routeProvider could offer a configurable function parameter that should defer rendering the initial route.

+1

+1

Here is a possible workaround: http://plnkr.co/edit/vi7mDjmD4NpZAoP7MVzr?p=preview
The idea is that Angular actually gives you the ability to create your own injector from a set of modules. In this case, I have delayed compiling the $rootElement until a bunch of promises (resolves) have resolved. This is a POC and there would be a number of other bells and whistles that would be needed to make this production ready. Apart from anything else you would need to catch errors in the resolves rather than just swallowing them.

+1

This is mostly a guide to whoever wants to give it a go; Currently the bootstrap process does the following:

  • Sanity checks
  • Creates the produced for $rootElement
  • Creates the injector
  • Compiles and links the $rootElement

On top, when creating the injector the steps are the following:

  • Traversing the dependency module tree and

    • Module initialization that would register the module constants, values, providers and other things

    • Running the config functions

    • Collection the run functions for later

  • Run all the run functions

There are a few key points:

  • All run blocks are executed before $rootElement is compiled
  • There are no digest cycles in the injector creation process, the first digest cycle is during the initial compilation of $rootElement
  • $q resolves promises during a digest cycle _only_

This is:

  • If there is a module that has a run function that returns a promise and the promise needs to be resolved before the app starts, then there will be digest cycles before the initial compilation (this may break existing apps)
  • A config block cannot return a promise as a config function is injected with providers, not with instances

Whatever solution, needs to handle these cases

+1
any news?
I just want to load some config settings before the app really starts.

+1

I would load some async data after bootstrap when all my factories are up and before compile.

@dagingaa : I think I would choose your solution as a workaround! thanks

  • 1

Update to @dagingaa : the for loop freeze my browsers, aint good...

https://jsfiddle.net/tuxmachine/t4d63vnw/

This is how I solved it for an OAuth token implementation, which required an initial ajax call to be resolved before initialising the rest of the app.

Won't work if you've got multiple asynchronous run blocks though

+1

+1

+1

It's been two years since issue was opened and there is still no good solution

@vladmiller There are solutions but perhaps you do not feel that they are good:

Putting non-trivial application work inside .run blocks makes it difficult to unit test your code. So it is not something we want to encourage. Moving this to the Ice Box as something we are not likely to implement.

@petebacondarwin I would disagree with you; everyone expect angluar to be simple and intuitive, instead you have to either implement your own async bootstrap module or bootstrap module in different place. In my opinion this makes angular more complex.

Can you also explain what do you mean when saying that async code in .run will make testing more difficult?

Apologies for my previous rude comment.
Thanks

@petebacondarwin I fail to see how it makes it difficult to test. if you place the initialization code in a service, you can just watch/mock/compare the results you'll be expecting from the http backend mock, regardless if it's being executed inside a .run block or not. having code exist outside angular due to work-arounds for the lack of async run is what makes it almost impossible to test

How it's going?

@petebacondarwin solution worked for me

Since there are workarounds and supporting async run blocks would add complexity to the bootstrap I don't think we will be implementing this feature.

jfcfxekzl5m
Simple feature but too hard to implement. 😕

+1 for this feature

+1

+1

@petebacondarwin solution works good.

+1. :(

+1

+1

+1

+1

We are not going to do this.

+100, all the work arounds are terrible.

@Eduardo-Julio - we are not going to implement this feature as it would make the bootstrap of AngularJS applications much more complex. Adding further +s will not help.

Was this page helpful?
0 / 5 - 0 ratings