Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Meteor delay compensation


May 10, 2021 Meteor


Table of contents


Delay compensation

In the last chapter, we introduced a new concept for Meteor: the built-in approach.

Meteor's built-in approach is a structured way to execute a series of commands on the server. In the example, we use the built-in method to ensure that new posts are marked by the author's name and ID, as well as the current server time.

However, if Meteor executes the built-in method in the most basic way, we'll notice some problems. Think about the sequence of events below (Note: Timestamp values are randomly generated for demonstration purposes):

  • 0ms: The user clicks the submit button and the browser triggers a call to the built-in method.
  • 200ms: The server changes the Mongo database.
  • 500ms: The client receives these changes and updates the page to reflect the change results.

If this is how Meteor works, it will have a short time difference to see the results of such an operation (the amount of delay will depend on the performance of your server). But we can't make these situations appear in web applications!

Meteor delay compensation

Delay compensation

To avoid this problem, Meteor introduced a concept called Latency Compensation. I f we put post of the collections/ This means that it exists on both the service side and the client side and is running at the same time!

When you use the built-in method, the client sends a request to the server to call, while also mimicking the server's built-in method to manipulate the local database collection. So now our workflow is:

  • 0ms: The user clicks the submit button and the browser triggers a call to the built-in method.
  • 0ms: The client mimics the built-in method of operating the local data collection and reflecting this change by changing the page.
  • 200ms: The server changes the Mongo database.
  • 500ms: The client receives these changes and cancels the impersonation operation just now, overwriting them (usually the same) based on the changes on the server. Changes to the page reflect this process.

This allows the user to see the change immediately. A fter the server's response returns for some time, the local database may or may not change significantly depending on the change request sent from the server database. Therefore, we should learn to ensure that local data is as consistent as possible with the server database.

Meteor delay compensation

Observe delay compensation

We can make a post to the call to the post built-in method. To do this, we'll use some advanced programming to put deferred objects into our built-in method calls through npm package futures

isServer to ask if the built-in method that Meteor is calling now is to be called on the client side (as a stub Stub) or on the server side. This stub is a simulation that mimics the built-in method that runs on the client side, while the "real" built-in method runs on the server.

So we'll ask Meteor if this part of the code is executed on the server side. I f so, we'll add a string after (server) post. If not, we'll (client) string:

Posts = new Mongo.Collection('posts');

Meteor.methods({
  postInsert: function(postAttributes) {
    check(this.userId, String);
    check(postAttributes, {
      title: String,
      url: String
    });

    if (Meteor.isServer) {
      postAttributes.title += "(server)";
      // wait for 5 seconds
      Meteor._sleepForMs(5000);
    } else {
      postAttributes.title += "(client)";
    }

    var postWithSameLink = Posts.findOne({url: postAttributes.url});
    if (postWithSameLink) {
      return {
        postExists: true,
        _id: postWithSameLink._id
      }
    }

    var user = Meteor.user();
    var post = _.extend(postAttributes, {
      userId: user._id, 
      author: user.username, 
      submitted: new Date()
    });

    var postId = Posts.insert(post);

    return {
      _id: postId
    };
  }
});

If we get there, this demo doesn't make much sense. Currently, it looks like you paused for 5 seconds after the post form was submitted, then moved on to the main post list and nothing else happened.

To understand why, let's take a look at the event handler submitted by the post:

Template.postSubmit.events({
  'submit form': function(e) {
    e.preventDefault();

    var post = {
      url: $(e.target).find('[name=url]').val(),
      title: $(e.target).find('[name=title]').val()
    };

    Meteor.call('postInsert', post, function(error, result) {
      // display the error to the user and abort
      if (error)
        return alert(error.reason);

      // show this result but route anyway
      if (result.postExists)
        alert('This link has already been posted');

      Router.go('postPage', {_id: result._id});  
    });
  }
});

We put the Router.go() routing function in the method callback function.

The behavior is usually correct now. After all, you can't jump users before you're sure if their post submission is valid, just because it can be confusing if you jump away immediately and then go back to the original post page a few seconds later to correct the data.

But for this example, we want to see the results immediately. So we changed the route to postsList (we can't route to the post yet, because we _id of the post outside of the method), moved it out of the callback function, and let's see what happens:

Template.postSubmit.events({
  'submit form': function(e) {
    e.preventDefault();

    var post = {
      url: $(e.target).find('[name=url]').val(),
      title: $(e.target).find('[name=title]').val()
    };

    Meteor.call('postInsert', post, function(error, result) {
      // display the error to the user and abort
      if (error)
        return alert(error.reason);

      // show this result but route anyway
      if (result.postExists)
        alert('This link has already been posted');
    });

    Router.go('postsList');  

  }
});

If we create a post now, we can clearly see the delay compensation. First, insert a post with (client) first post in the list, linked to GitHub):

Meteor delay compensation

Then, after five seconds, it is replaced by the real post document inserted by the server:

Meteor delay compensation

The client collection has built-in methods

From what you said above, you might think built-in methods are complicated, but in fact they can be quite simple. In fact, we've used three very simple built-in methods: the collection's insert update remove

When you define a server collection called 'posts' implicitly defined these three posts/insert posts/update posts/delete In other words, when you call Posts.insert() you're already calling the delay compensation method to do both:

  1. Check to see if we are allowed to allow by calling deny to the allow and deny methods (however this does not need to occur in the simulation of the built-in method).
  2. Actually modify the underlying database.

The built-in methods call each other

You may have realized that when we insert a post built-in method calls another built-in posts/insert How does this work?

When the impersonation method (the built-in method of the client version) starts running and the simulation method insert (inserted into a local collection), we don't call it the real post be inserted the same way. insert

Therefore, when the server-side post method insert there is even more unnecessary to worry about the client impersonation method, which is sure to be inserted smoothly on the client side.

As before, don't forget to restore your changes before reading the next chapter.