-
-
Notifications
You must be signed in to change notification settings - Fork 408
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
Promise based initializers #572
Conversation
before the application begins routing. | ||
|
||
For example, when booting an Ember application in an embedded environment, it may be necessary to | ||
listen for events from the environment to determine the initial state to determine the initial URL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would using the beforeModel in the application route work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this particular use case, the application route is too late because there are subtle differences between what it means to start routing at a URL and what it means to redirect to a URL. When an Ember app runs in a browser, it does the former.
That said, I think promise based initializers will have benefits beyond just determining the initial state of the application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't redirect from the application route though. It,s the parent of everything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redirecting is just a fancy word for saying " cancel the current transition and start a new one". You can intercept a transition in the ApplicationRoute
that would have ended at /foo
and and redirect to /bar
, for example.
|
||
Currently, the earliest place that developers can write asynchronous code is in the ApplicationRoute. | ||
For some use cases, this is not early enough, and it is useful to be able to run asynchronous code | ||
before the application begins routing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have known use cases where the Application route "is not early enough"? I'm not doubting the truth of this statement, but having an example use case or cases would make it easier for me to wrap my head around how this would be useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! If you have a webview embedded in a native layer that sends you events (say when the native app is launched via deeplink). Capturing that event and operating on it is an asynchronous operation. If you wait till the ApplicaitonRoute
, the Ember app has already started routing, so the only option to get to the desired route is redirect()
. That's starkly different from what happens when the Ember app is initialized at a subpath in the browser, however and it can cause redirect hell if you receive more than one of these events.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even accepting this case as gospel, I don't see why the changes proposed in this RFC would be needed. The specific case seems to be that you want to avoid autobooting your application (until these events have come back), that is already a supported operation (without changes AFAICT).
This feels to me to be introducing a new feature to Ember for what seems like a very very specific/targeted scenario for a single application (that already has argubly better solutions).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
skipping autoboot is very much NOT the happy path, regardless of what the intention of the visit
API was. The reason I introduced this API is because initializers are one of the few non-async code paths in Ember APIs today and it seems like a good thing to have them regardless of the use cases I've listed here.
We do have other use cases also:
- Making a network call that sets some cookies before we do anything else
- Initializing 3rd libraries that have async APIs (or make their own async calls)
This stuff can happen in the route, but I'd argue that not all of them should happen in a route, if we have the concept of an initializer. If initializers weren't a thing at all, I'd focus more on the manual boot scenario and improving that.
Really like this! Could we add an afterAsync key or something like that so that one initializer could wait for another one to resolve? |
implements a method that loops through the loaded initializers and executes the exported function. | ||
|
||
Initializer and Instance Initializer files each export an object with an `initialize` property | ||
that references a function. For backwards compatibility, an `async` property should be added, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the initialize
return type can be detected do we still need async
? I think handling async initializers based on this option or the actual return type affects unresolved questions below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call out. I debated over the need for this key too. The main reason it's useful is that you cannot detect the return type without running the initializer. The purpose of knowing earlier is to opt the user into the promise-based flow (the whole thing would need to be wrapped in a promise). Without any async
declarations, existing apps would be able to retain the exact same behavior as they have today.
I'd be more of a fan of deprecating intializers as a whole. I don't think they're needed. Ime, everything initializers can do is better handled in the application route. Either in a before model, or the init/constructor. |
Few thoughts:
In any case, I think removing initializers completely is a bit out of scope here and would require a lot more discussion and feedback from the community. This seems like a pretty easy win that would put us on the path to a fully async app boot, and then we can fiddle around with where code lives. |
I think the existing As a side note, I think the better solution to ordering is the approach in #573, rather than continuing down the before/after path. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned in one of the other RFCs (#573 I think?), I'm personally pretty opposed to making any changes to the initializer system that are not directly targeted at making its removal easier.
Another example may be to interact with 3rd party libraries that need to be initialized early but | ||
have an asynchronous API. | ||
|
||
## Detailed Design |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some important details (to me at least 😸) are not mentioned here:
- What happens if you say you are
after
an intializer that is async, are you called after its promise resolves? The RFC should explicitly state this behavior. - What is the benefit of adding
async: true
(vs doing something like promise-map-series)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- yes
async: true
was dreamed up for opt-in beahvior and to make it easier to sell :D. I don't know what promise-map-series is, but it sounds like wrapping each initializer in a promise and executing them in order? i'm also fine with that, but I was worried that could have performance implications or could make it possible for some other framework code microtasks to run in between initializers. If neither of those are concerns, I have no problems with it.
|
||
Currently, the earliest place that developers can write asynchronous code is in the ApplicationRoute. | ||
For some use cases, this is not early enough, and it is useful to be able to run asynchronous code | ||
before the application begins routing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even accepting this case as gospel, I don't see why the changes proposed in this RFC would be needed. The specific case seems to be that you want to avoid autobooting your application (until these events have come back), that is already a supported operation (without changes AFAICT).
This feels to me to be introducing a new feature to Ember for what seems like a very very specific/targeted scenario for a single application (that already has argubly better solutions).
should default to `false` if omitted. If true (or truthy), the Application/ApplicationInstance would | ||
expect a promise to be returned from the function and wait for the promise to resolve | ||
before running the next initializer in the queue. The value resolved from the promise would be | ||
ignored. Promises that reject would throw an exception in development and be caught in production |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and be caught in production
What does this mean? If initialization of an application fails, I cannot reason about how to make that app functional again...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's up to do the developer to determine that. For example, we make a network request at the beginning to set some cookies. Without those cookies the app is perfectly functional, but could have unintended consequences later. Catching errors in production would just be a failsafe for apps who opts into this feature, but doesn't think through that. I'd be fine with removing this part.
If implemented, future RFCs could: | ||
|
||
- change the default value of the `async` property to `true`. | ||
- deprecate `deferReadiness` and `advanceReadiness` entirely. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍👍 - Seems like a great thing to do when we deprecate initializers 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍. It's up to core team what order to do all this in. This RFC seemed like a good path to "just use JS" that didn't require changing the initializer paradigm!
@chancancode do you have any thoughts here (considering you did the seminal work for async boot in emberjs/ember.js@1a42386)? |
Closing this down! |
rendered