11

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?

fuzzybabybunny
  • 5,146
  • 6
  • 32
  • 58

2 Answers2

21

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.

Jordan Davis
  • 1,271
  • 14
  • 24
  • Gotcha. Thanks for the explanation. I thought `defer` could be something that I could use to wait for DOM elements to load before initializing 3rd-party code, since this post mentions it - https://github.com/meteor/meteor/issues/2176 - : `I use Meteor.defer() quite regularly. Anytime I have an instruction in my code that doesn’t seem to execute and then I run that same instruction in the console and it works, I wrap it in my code with Meteor.defer() and that usually works. Typically, it seems to be cases where something in the DOM that I’m depending on has not yet rendered. ...` – fuzzybabybunny Jul 23 '15 at 06:38
  • I don't understand how it can be used to get the effect of a `rendered` callback. If you use `defer` to make the code `async` i.e. `non-blocking`, the code will simply immediately execute without waiting for anything... it's the exact opposite of waiting for something to complete (like DOM rendering). – fuzzybabybunny Jul 23 '15 at 06:40
  • It actually makes sense... running a `defer` removes it from the current execution queue, and since js is single threaded, it just gets run when whatever is in the execution queue finishes up - so it works as a bit of a delay (in your first comment) but does not immediately execute (because single threaded) in your second comment. – Jordan Davis Jul 23 '15 at 06:41
  • here's a fiddle to show how that works https://jsfiddle.net/k8eg3dk1/ look at the console. You will see where the `setTimeout` runs. – Jordan Davis Jul 23 '15 at 06:52
  • Thanks. FYI I made another, more general post about JS async: http://stackoverflow.com/questions/31580437/how-is-js-both-non-blocking-asynchronous-but-single-threaded – fuzzybabybunny Jul 23 '15 at 07:09
  • Yeah, js functions in a timeout wait till the current stack is clear, basically. It's best to just think of it as a single thread *almost* all the time. Have a good one! – Jordan Davis Jul 23 '15 at 07:21
-1

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]
iamhimadri
  • 532
  • 1
  • 5
  • 20
  • 1
    i am also assuming this is the one of the great way to solve this kind of asynchronous execution issue, hope this may help others. and also let us know if any other possible ways...happy coding. :) – iamhimadri Jul 23 '15 at 07:11
  • 1
    -1 That is asking for trouble... You are simply lucky that your method call is executing fast enough (I'm sure it has a stub). There is no way to be sure that the defered function will be called after the method's callback. The defered function will just run after all currently planned executions are done. The callback will not be planned if you are waiting for an answer from the server just like you try to imply. – Salketer Nov 17 '16 at 10:54