No, that’s good to know! I didn’t know since I haven’t tried cordova/phonegap yet. The more examples, the better! Thanks!
Based on the code for the client-side Meteor.startup, it’s not so useful. It doesn’t relate to any Meteor-specific APIs being ready or anything, just DOMContentLoaded (which means not necessarily templates rendered).
var queue = [];
var loaded = document.readyState === "loaded" ||
document.readyState == "complete";
var ready = function() {
loaded = true;
while (queue.length)
(queue.shift())();
};
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', ready, false);
window.addEventListener('load', ready, false);
} else {
document.attachEvent('onreadystatechange', function () {
if (document.readyState === "complete")
ready();
});
window.attachEvent('load', ready);
}
Meteor.startup = function (cb) {
var doScroll = !document.addEventListener &&
document.documentElement.doScroll;
if (!doScroll || window !== top) {
if (loaded)
cb();
else
queue.push(cb);
} else {
try { doScroll('left'); }
catch (e) {
setTimeout(function() { Meteor.startup(cb); }, 50);
return;
};
cb();
}
};
On the server-side, the code is even shorter:
Meteor.startup = function (callback) {
if (__meteor_bootstrap__.startupHooks) {
__meteor_bootstrap__.startupHooks.push(callback);
} else {
// We already started up. Just call it now.
callback();
}
};
but I have a feeling it’s more meaningful on the server-side…
@awatson1978 @glasser In the client-side Meteor.startup, the line
setTimeout(function() { Meteor.startup(cb); }, 50);
is for IE<9 (based on this). It doesn’t guarantee that all callbacks will be fired in the same order they are added because callbacks can be added at any point in time before doScroll() starts not throwing exceptions. The callbacks are ordered based on when they are randomly added and when doScroll randomly starts works.
Here’s a PR: https://github.com/meteor/meteor/pull/4156
All along I thought I had understood the sequence of things, like when Meteor.startup runs.
And to prove it I created this meteorpad to demonstrate it. But to my surprise the sequence of events does not make sense.
A helper in a template fires before the template is rendered, even before Meteor.startup, and the template gets rendered before it is created? And who new Session.equals is not reactive?
Template.leaderboard.helpers({
allRendered: function() {
var x = 0;
// without the next line, this helper function gets called only once
x=Session.get('renderedPlayers');
console.log('allRendered '+x+' '+numberOfPlayers);
return Session.equals('renderedPlayers', numberOfPlayers);
},
})
If someone wants to take a stab and explain why this is, I am all ears.
@jamgold The first template helpers are fired in order to generate the initial HTML. Meteor.startup should fire after that when that DOM is ready, at least that’s what I’ve heard somewhere in the community. I’m not sure exactly how this happens. It may be that document.write is being used for the first templates? Or perhaps the templates are appended to the DOM before the final </body>
is read by the browser? I don’t know, but if the first templates are indeed rendered before Meteor.startup, then it makes sense you see allRendered before Meteor.startup.
I think “first templates” might be the ones you’ve put inside of the <body>
directly, so in this case that’s leaderboard
, and since that’s the only one, it makes sense that you see only a single “allRendered” output before the Meteor.startup output, and then the rest of the templates in leaderboard
are rendered after.
I’m curious to know if this is precisely what happens, but it seems like it is based on what I’ve read/heard somewhere.
Here’s how I think of it:
Your application is run within the browser, but it also requires the Meteor object. The Meteor
object is like a large balloon inside a box. Absent other objects within your box, if you blow up the balloon, it will expand to take the shape of the box (minus a little space in the corners). Similarly, absent things that one has attached to the window
object, the Meteor
object is proportionately the entirety of your application.
Only, the Meteor
object isn’t a balloon. To use a car analogy, it’s the chasis and frame of your car. And that makes the Meteor.startup()
method like the ignition system in your car.
Using that analogy, it’s possible to replace the tires on a car or change the oil without having the car engine ignited, right? That’s what’s happening when you see that Templates are getting rendered before the Meteor.startup().
The Meteor
object gets attached to the window
object, but so does the Session
and Template
objects. Which means that there’s a bit of chance that they can get initialized before Meteor
does, and start to process things. But a tire or a bottle of oil don’t drive themselves. Without the Meteor
object, they just kind of sit there, waiting for the app to initialize. But when the Meteor
object initializes, and then the Meteor.startup()
is called, everything starts up and the Templates get picked up and put into place; the data subscriptions get data to the client, and the templates can finally render correctly.
To the extent that minimongo is a client-side Mongo replica set, this can also be considered database flapping, by the way. (Being reactive, the templates will sometimes flap a bit before the data subscriptions get populated and they have data to grab onto. This can be managed with defensive programming techniques.)
I documenting this in the Event Cycle page back in the 0.7 days, and have tried to keep it up to date during the subsequent API changes. I’m not sure how well this explains things, or how accurate it really is, but it should hopefully sort of explain things.
Meteor Event Cycle - Advanced
Also, take the term ‘render’ with a grain of salt. It’s not rendering in the sense of the term that animators or video device drivers use the term. It’s not rendering in the sense of a double-buffered render graphics pipeline. As I understand it, It’s ‘render’ more in the sense of ‘assemble a DOM tree’.
Session
is a global instance of ReactiveDict
: https://github.com/meteor/meteor/blob/devel/packages/session/session.js#L1 and the documentation for ReactiveDict
's equals
method states that it will be reactive as long as the state changes.
@robfallows that is why I was surprised that the equals method did not trigger when the evaluation of equals changed, unless I also add a Session.get
into the helper.
@awatson1978 thank you for the lengthy response and analogy. In general it all makes sense; the seemingly unpredictable sequence of onCreated vs. onRendered does raise eyebrows, however.
On the server, Meteor.startup
appears to be executed after all other server code is executed. This allows access to globals defined elsewhere, for example.
Say we have model.js
which defines a collection Notes
:
Notes = new Mongo.Collection('notes');
In server/app.js
, the following will error out with ReferenceError: Notes is not defined
:
Notes.allow({...})
If wrapped in Meteor.startup
, the code will execute correctly, after Notes
becomes available:
Meteor.startup(function () {
Notes.allow({...});
});
Curiously, this isn’t explained in the full API docs (CC @sashko, I’ve just sent a PR), but is alluded to in the basic docs, which I assume many non-beginners never check, thinking the full API includes the information from basic.
Corrected link to deprecated cookbook “Meteor Event Cycle” article:
Two years later, and there’s not really any formal documentation on what should and shouldn’t be put inside a Meteor.start
handler.
The formal recommendation regarding globals nowadays is not to use them, and instead import
what dependencies we need, so by the very nature of modules, we may not need Meteor.startup
if we can import things instead.
@benjamn @sashko Can you guys get someone at MDG to update the Meteor.startup
docs and formerly explain when exactly, if any, that we still need Meteor.startup
?
Maybe it really isn’t needed any more for module-based apps, unless some code is still in the old “automatically loaded, not a module” form.
Are there still cases when we really still need Meteor.startup
?
Maybe the over-impending idea here is not to include API who’s usage is guess work, but to rather specify what we should and shouldn’t do. Maybe Meteor.startup
doesn’t have any good use anymore, and we should just import stuff and we can remove Meteor.startup
from the docs.
As @elie said, that was an undocumented use-case for Meteor.startup
, but I would imagine that we just import those APIs nowadays (I still haven’t tried those APIs so I’m giving an educated guess).
It’s mostly useful as a callback for document.ready and also waits until the ddp connection is initialized I think. Definitely worth some extra docs!
Yes I mainly use it for document.ready. I don’t really know exactly what other cases to use it.
I currently only use it to ensure an onLogin
hook is set up before anything else loads. Not really sure if it’s necessary but better safe than sorry.
Meteor.startup(fuction () {
Accounts.onLogin(function () {
// on login stuff
});
});
The thing is that when the Meteor.startup callbacks are fired, everything is loaded already…
After I removed the startup
wrapper, my route related expressions were no longer working, claiming route was undefined. But after I wrapped it in startup
again, it worked.
A real blackbox it is!