[SOLVED] When should we use Meteor.startup()?

I read the docs on Meteor.startup but it’s not entirely helpful. It’d be nice to update the docs with more details on what’s it’s really useful for, not just “On a server, the function will run as soon as the server process is finished starting. On a client, the function will run as soon as the DOM is ready.”.

Based on that, why not just use Template.x.render on the client-side? Perhaps the docs for Meteor.startup should mention Template.x.render?

What are some really good use cases for using Meteor.startup where had it not been used things just wouldn’t work?

4 Likes

One case it is necessary (although guessing this isn’t what you were looking for):

Any functionality which relies on a Cordova/Phonegap plugin should wrap code in a Meteor.startup() block. For example, when using the Cordova/Phonegap geolocation plugin:

Meteor.startup(function () {
    // The correct way
    navigator.geolocation.getCurrentPosition(success));
});

// Will not work
navigator.geolocation.getCurrentPosition(success));

From:

5 Likes

No, that’s good to know! I didn’t know since I haven’t tried cordova/phonegap yet. The more examples, the better! Thanks!

1 Like

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…

1 Like

@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.

@glasser @awatson1978 @arunoda Do you know?

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’.

10 Likes

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.

1 Like

@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.

2 Likes

Corrected link to deprecated cookbook “Meteor Event Cycle” article:

1 Like

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?

1 Like

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!

4 Likes

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
  });
});
1 Like

The thing is that when the Meteor.startup callbacks are fired, everything is loaded already…

1 Like