There’s a lot of confusion going on in this thread. Let’s take it piece by piece…
this.ready()
is what tells the client that the publication is “initially done” or better yet “done for now” (the server can send more documents later on). In the following example the client will never know when the server is done sending the initial batch of documents. That’s because even though the server has finished sending the documents, it’s not sending the ready notification.
// Server
Meteor.publish("people", function () {
this.added("people", "1", { name: "Alan" });
this.added("people", "2", { name: "Brito" });
// Not sending the ready event!
});
// Client
Template.body.onCreated(function () {
this.subscribe("people", function () {
console.log("This will NOT print.");
});
});
If we add this.ready()
the client then knows when the server is done sending the initial batch:
// Server
Meteor.publish("people", function () {
this.added("people", "1", { name: "Alan" });
this.added("people", "2", { name: "Brito" });
this.ready();
});
// Client
Template.body.onCreated(function () {
this.subscribe("people", function () {
console.log("This WILL print");
});
});
Now regarding the following statements:
this.ready();
return [];
return People.find( { "_id": "-1" });
They all send the ready notification to the client but for different reasons.
this.ready()
is the most straightforward and it just sends the ready notification to the client.
When you return an array (in a publication), Meteor loops through the array of cursors and sends an added document to the client for each element in the cursors. When it’s done it then sends the ready event to the client. That’s why return [];
does sends the ready notification, because it loops through the empty array, it doesn’t send any records to the client and then fires the ready event.
When you return a cursor, Meteor goes to the database, fetches the records, sends an added document for each record, and then sends the ready notification. return People.find( { "_id": "-1" });
sends the ready notification because Meteor goes to the database, doesn’t find/send any records, and then fires the ready event.
So the following two snippets are equivalent (Up to the ready point because the first one will keep sending updates to the client while the second one doesn’t send anything else):
Meteor.publish("people", function () {
return People.find();
});
===
Meteor.publish("people", function () {
var self = this;
People.find().forEach(function (person) {
self.added( "people", person._id, person );
});
self.ready();
});
And thus this.ready()
is better than return [];
which is better than return People.find( { "_id": "-1" });
Regarding the performance optimization I used at the end of my presentation (https://www.youtube.com/watch?v=RmjYw4G4ImQ). That only applies when the publication returns an aggregate (in the presentation we do a reactive aggregate). Without the initializing
variable the server would send one “aggregated” document for each element in the array.
For example, if there are 50 names and they all start with “A”, without using the initializing
optimization, the server would loop through the collection, pick up the first name and send { letter: "A", count: 1 }
, then pick the second and send { letter: "A", count: 2 }
, and so on. With the optimization the server performs the aggregate and then sends the final count once: { letter: "A", count: 50 }
As for executing this.ready()
vs throwing an error when you know the collection is empty, it depends. If the client isn’t supposed to subscribe to the publication then you should throw an error. If you want the client to subscribe to a publication and let the server decide what to send back, then use this.ready()
.
I hope this helps.