Initializers that return promises


#1

Assume I have the following initializers:

App.initializer
  name: "first"
  initialize: (container) ->
    $.get('/api/connect').then (data)
      # do some work

App.initializer
  name: "second"
  after: "first"
  initialize: (container) ->
    # do something that requires result of first initializer

As it currently stands the initializers will run in the correct order, but the second initializer will (potentially) run before the ajax callback has completed. If you have an asynchronous initializer that should block boot you can use deferReadiness, but for the above case that won’t work, you need to combine the two initializers into one (I believe).

My proposal is that initializers should respect promises, and only execute the next initializer if initialize returns null and/or a resolved promise.

Thoughts?


#2

/cc @stefan any thoughts?


#3

it will absolutely run the 2nd initializer, and all initializers before any of the ajax callbacks complete. As the initializers graph is walked synchronously, and promises always resolve on the next turn. So no scenario may exist in-which this is not true.


I believe that we have two great tools (promises, and initializers) which can, if needed, work well together. Combining them will likely just be a negative complexity increase.

Initializers are ordered based on how their DAG is described, and promise resolutions are ordered based on how the promise graph is described, in conjunction with the order resolutions occur. This provides us with tooling to resolve the sync configuration ordering, and tooling to resolve asynchronous dependencies.

In the scenario were you must have multiple initializations that share some or depend on some state, if that state is asynchronous in nature, the references to this state should be promises. Then given that all async state references are modeled as promises, when the initializers are walked they each build up the async deps. Once the initializers are all traversed the promise graph asynchronously resolves itself. Additionally if app boot needs to be synchronized advance/defer readiness can be used.


#4

That all makes sense. I was thinking that maybe the results of the initializers could be linked in the order that they are currently processed, so that they will be run asynchronously, but in the correct order (if that makes sense?). However, I agree that it’s probably negative complexity, and you can stash the dependencies between the promises globally and do it that way, which achieves the same thing.


#5

Please, forgive my ignorance, but what’s DAG?


#6

directed acyclic graph


#7

initializers are run in topsort of the dag they create:

https://github.com/emberjs/ember.js/blob/master/packages/ember-application/lib/system/dag.js


#8

Thank you for this response. Its pretty amazing the kinds of things that make Ember work.


#9

@stefan what do you about using ApplicationRoute#model hook to call execute async code? As far as I’m aware, you would rarely ever actually use ApplicationRoute#model, but it seems to be a good place load data that you need to run the app. I’m writing some code that looks like this

App.ApplicationRoute = Ember.Route.extend({
  model: function() {
     return Ember.RSVP.hash({
        posts: Em.$.getJSON('posts.json')
     }).then(function(data){
        App.reopen(data);
       });
  }
});

Do you see anything wrong with this?


#10

the router is likely one of the best places to load async code. That being said, our current loading state solution is sub-optimal, and will be getting some more attention shortly.