Can someone explain how Meteor.defer() works? - meteor

So... Meteor.defer(function(){ // stuff }) isn't in the docs:
https://github.com/meteor/meteor/issues/2176
But this links seems to say that it's simply equivalent to
Meteor.setTimeout(function(){ // stuff }, 0);
If that's the case, how does this do, um, anything? It's basically saying "wait for 0 ms and then run the function".
So... it runs the function instantly.
What am I missing here? Is this kind of like Tracker.afterFlush or something? Does it somehow wait for "things" (what kind of things?) to finish before running?

I see Meteor.defer() a lot on SO being used as a bit of a hack on added helper methods to run after the dom is (somewhat) loaded - basically to get the same effect as running code inside of a Template.foo.rendered method.
However, the main (and best) use of Meteor.defer is to run a task asynchronously.
Let's say we have an app where we are sending an email. On the server, it may take several seconds for that to process inside of a meteor method, slowing down your application drastically. However, if you wrap that process in a Meteor.defer the email processing won't block execution, the email still sends (when it gets a chance, not instantly), but everything runs much faster, since the code that follows isn't waiting. There is a great example lesson about deferring execution at Bulletproof Meteor.
You can actually get the same effect with a setTimeout(f,0) - if you have a slow function, you could wrap it in the setTimeout and the rest of the code will complete, and 'defer' the slow process in the timeout, so although it doesn't seem like it, setTimeout(f,0) does actually have a pretty useful purpose!
To see an example of this in action, here's a fiddle, open the console and watch where 'foo' logs.

I faced some issue in my project because of asynchronous callback. Inside onCreated i was making a server Meteor.call and set the response inside reactiveVar. And i was doing something inside onRendered with that reactiveVar. Every time reactiveVar was showing undefined.
So i used Meteor.defer(function(){...}) inside onRendered and it sloved my issue.
Here is some demo with and without using Meteor.defer()
Template.myTemplate.onCreated(function () {
var instance = this;
instance.myTemplateModel = new ReactiveDict();
Meteor.call('all-user', function(err, res){
if(res){
console.log('inside callback');
instance.myTemplateModel.set('users', res);
}
});
});
Template.myTemplate.onRendered(function () {
var instance = this
console.log('rendered start');
Meteor.defer(function(){
console.log(instance.myTemplateModel.get('users'));
});
console.log('render end');
});
Console:
/*Without Meteor.defer()*/ | /*With Meteor.defer()*/
render start | inside callback
undefined | render start
render end | render end
inside callback | [Object, Object, Object]

Related

How can I use collection.find as a result of a meteor method?

I'm trying to follow the "Use the return value of a Meteor method in a template helper" pattern outlined here, except with collections.
Essentially, I've got something like this going:
(server side)
Meteor.methods({
queryTest: function(selector) {
console.log("In server meteor method...");
return MyCollection.find(selector);
}
});
(client side)
Meteor.call('queryTest', {}, function(error, results) {
console.log("in queryTest client callback...");
queryResult = [];
results.forEach(function(result) {
// massage it into something more useful for display
// and append it to queryResult...
});
Session.set("query-result", queryResult);
});
Template.query_test_template.helpers({
query_test_result: function() {
return Session.get("query-result");
}
});
The problem is, my callback (from Meteor.call) doesn't even get invoked.
If I replace the Method with just 'return "foo"' then the callback does get called. Also, if I add a ".fetch()" to the find, it also displays fine (but is no longer reactive, which breaks everything else).
What gives? Why is the callback not being invoked? I feel like I'm really close and just need the right incantation...
If it at all matters: I was doing all the queries on the client side just fine, but want to experiment with the likes of _ensureIndex and do full text searches, which from what I can tell, are basically only available through server-side method calls (and not in mini-mongo on the client).
EDIT
Ok, so I migrated things publish/subscribe, and overall they're working, but when I try to make it so a session value is the selector, it's not working right. Might be a matter of where I put the "subscribe".
So, I have a publish that takes a parameter "selector" (the intent is to pass in mongo selectors).
On the client, I have subscribe like:
Meteor.subscribe('my-collection-query', Session.get("my-collection-query-filter"));
But it has spotty behaviour. On one article, it recommended putting these on Templates.body.onCreate. That works, but doesn't result in something reactive (i.e. when I change that session value on the console, it doesn't change the displayed value).
So, if I follow the advice on another article, it puts the subscribe right in the relevant helper function of the template that calls on that collection. That works great, but if I have MULTIPLE templates calling into that collection, I have to add the subscribe to every single one of them for it to work.
Neither of these seems like the right thing. I think of "subscribing" as "laying down the pipes and just leaving them there to work", but that may be wrong.
I'll keep reading into the docs. Maybe somewhere, the scope of a subscription is properly explained.
You need to publish your data and subscribe to it in your client.
If you did not remove "autopublish" yet, all what you have will automatically be published. So when you query a collection on client (in a helper method for example), you would get results. This package is useful just for quick development and prototyping, but in a real application it should be removed. You should publish your data according to your app's needs and use cases. (Not all users have to see all data in all use cases)

Meteor wrapAsync works, but restarts server after completion

I'm trying to learn how to use Meteor's wrapAsync helper. I want to use it to call an async function sequentially (not simultaneously). Though my code works, when it completes, the server restarts, which makes me wonder if there is a bug in my code. Unfortunately, there is no message saying why it restarted. Am I missing something?
The code is below and also on my MeteorPad demo.
var doAsyncIO = function (num, callback) {
setTimeout( function () {
// wrapAsync will use the following callback as the basis of returning results synchronously
callback(null, 'This is delayed result #' + num ); // nodeJS form: cb(error,result)
}, 2500); // wait 2.5 seconds before returning result
};
// Now create a synchronous version of doAsyncIO that sleeps, not blocks
// so that the CPU can do other tasks while waiting for the result
var doSyncIO = Meteor.wrapAsync(doAsyncIO);
// Now call doSyncIO twice sequentially (not simultaneously)
console.log('\n*** Starting first call to doSyncIO');
var result1 = doSyncIO(1);
console.log('result1:', result1);
// The following block runs after result1 is set (which is what I want)
console.log('\n*** Starting second call to doSyncIO');
var result2 = doSyncIO(2);
console.log('result2:', result2);
After a code change and after the console logs appear on the terminal, the terminal says the server restarts with no reason provided which makes me wonder if there is a bug in my code!
2015-04-19 Update: the restart message may not actually indicate a problem since it only shows up after a code change. Perhaps it's simply the standard restart message. Nevertheless, it seems like the restart message should really occur before the console logs.
Feel free to comment or fork my MeteorPad demo though to see if it's something else.

Correctly stopping an autorun() function when template is destroyed

I have a template that contains a chart, rendered using MorrisJS. The chart should update when the currentData session variable is changed, so I have made it a reactive data source with:
Template.chart.rendered = function() {
var template = this;
Deps.autorun(function(c) {
// Stop if the template is removed from the dom
// Q: Is this really right?
if(template.__component__.dom.parentNode() === null) {
c.stop();
return;
}
var results = Session.get('currentData');
// ... render a chart with `results` as the data
Morris.Bar({element: template.$(".chart-container"), data: results, ...});
});
};
Notice how I do a fairly horrid check for when to stop the autorun above. This was necessary because without this, when I navigate away from the page using the template (I'm using iron-router) to another page and back, I get warnings in the log like "Can't select in removed DomRange". I'm pretty sure this is happening because the template instance is removed, but the autorun is still running.
I feel like I'm doing something wrong here, though. Is there (a) a better place to put the autorun so that it doesn't have this problem or (b) a better way to stop the computation when the template instance is removed from the DOM?
I tried to find a way to do it with created and destroyed handlers, but I couldn't figure out how.
Tracker.autorun returns a handle that you can store as a template instance property, then call its stop method in the onDestroyed lifecycle event.
Template.chart.onRendered(function(){
this.computation = Tracker.autorun(function(){...});
});
Template.chart.onDestroyed(function(){
this.computation.stop();
});
EDIT 29-09-2014
In newer versions of Meteor (0.9 onward), there is a new autorun function available on template instances which provide simpler code to achieve the same result : no need to store and stop the computation manually, this is taken care of by the framework.
Template.chart.onRendered(function(){
this.autorun(function(){...});
});
With the new autorun
Template.chart.onRendered(function(){
this.autorun(function(computation){
...
computation.stop();
});
});
but with this autorun, when chart template is removed from the DOM it is removed automatically.
This is in Meteor documentation here:
The Computation is automatically stopped when the template is destroyed.

How to delay AngularJS App Initialization?

I have some data that is being processed asynchronously in the background and want to delay the initialization of the entire AngularJS application until this finished.
BackgroundData.initialized is a Q promise, so something like this:
BackgroundData.initialized.then(AngularDoYoStuff)
The problem I run into is the home page's controller starts its initialization procedure, hits BackgroundData and either it has the wrong/no data.
What function can I wrap Angular's initialization in so, instead of just dom-ready, it waits for both dom-ready and BackgroundData.initialization?
UPDATE
I have gotten closer with the documentation on manual bootstrapping:
angular.element(document).ready ->
setupGA()
window.BackgroundData = new DataMachine()
BackgroundData.initialized.then ->
angular.bootstrap(document)
But when the controller files load (after this file), they are still getting initialized before BackgroundData is defined
UPDATE 2
Removing the ng-app directive in the HTML seems to have fixed the problem (since that was telling Angular to auto-init) but now it just ignores all of my angular.module calls
The problem was that I had left the ng-app directive in the html tag, which tells Angular to auto-initialize that scope. Removing it allowed my manual initialization to run correctly.
as Chris mentioned, it can be done with angular.bootstrap and not mentioning the ng-app:
<div id="appArea" ng-controller="someCtrl">
{{name}}
</div>
<script>
angular.module('someApp', [])
.controller('someCtrl', function($scope) {
$scope.name = "test name";
})
setTimeout(function() {
angular.bootstrap(document, ['someApp']);
}, 2000);
</script>
One way to approach this if you are using routes in your app is to have the app initialize but wait on defining routes until the data is available.
I.e. provide a user-friendly 'loading' message, and then load functionality after.
You are able to inject $route where required, and then call something like:
$route.routes["/RouteName/:param"] = {templateUrl:"template.html", reloadOnSearch:true, controller:"ControllerName"};
After that, call $route.reload() or $rootScope.apply() to refresh.
More information at https://groups.google.com/forum/?fromgroups=#!msg/angular/AokZpUhZ6mw/x2kPIN2VAC0J

yepnope's complete callback question

I am using a "2-step view", where I have a layout template (common for all pages) with yepnope's load (in the html head) of jQuery and some plugins. Something like:
yepnope(['/path/to/jquery.js', '/path/to/jquery.plugin-common.js']);
Sometimes I need another plugin, so within the inner template I do additional (in the html body):
yepnope('/path/to/jquery.plugin-additional.js');
Now that I need to do the actual js magic, can I safely do just:
yepnope({
complete: function(){...}
});
So, the questions are in fact two:
Is the complete callback fired upon the load completion of the global resources stack? So it's safe to "register" this complete callback anywhere assuming that all needed resources have been registered before?
Can I safely call yepnope just with the "complete" callback option? I mean, as long as I'm not "testing" anything and my resources have been registered already...
I have tried it and it worked, but I'm not fully aware if it's internals, so I just want to make sure that I'm not doing something wrong... Thanks in advance.
--
And one last thing. The manual under preload! says:
yepnope({
load: 'preload!jquery.1.5.0.js',
callback: function (url, result, key) {
window.jQuery; // undefined (but it's cached!);
}
});
Can you please explain what is this about? I am completely missing the point here...
I can help on the preload! question.
The idea of preload! is that yepnope will download the file but will not execute it.
It transfers the jQuery file, but it will still be undefined after callback is being called, as it was not injected as a script into the page.
In my opinion, you are doing this wrong. I'm surprised that it work, but maybe your scripts are loaded before the call of the "complete" function. I think you should do that:
yepnope({
load: ['/path/to/jquery.js', '/path/to/jquery.plugin-common.js']
callback: {
"jquery.js": function () {
console.log("jquery loaded!");
},
"jquery.plugin-common.js": function () {
console.log("plugin loaded!");
}
}
});
And for the additional plugin in the html body:
yepnope({
load: '/path/to/jquery.plugin-additional.js'
callback: function () {
$(document).ready(function(){
console.log("plugin-additional loaded!");
});
}
});
Of course, replace console.log() by your code related to each plugin that you can safely execute in this context.
for the last question, i can't say anything, because i didn't succeed to make preload! working, maybe it's buggy, maybe i didn't understand how it work...

Resources