Does an autorun() unsubscribe a subscription if it is not renewed?

I have the following code inside the onCreated() method of a template:

// Subscribe to notifications
  instance.autorun(function() {
    var limit = instance.limit.get();
    var subscription = instance.subscribe('notificationsForUser', {
      limit: limit
    });
  });
  // Get user records for the senders
  instance.autorun(function() {
    var cursor = notificationsCollection.find();
    var notifications = cursor.fetch();
    var userIds = _.uniq(_.pluck(notifications, 'sender'));
    if (_.difference(userIds, lastUserIds).length === 0) {
      return; // lastUserIds is a global variable
    }
    lastUserIds = userIds;
    instance.subscribe('usersById', userIds, {
      fields: {
        'username': 1,
        'profile.firstName': 1,
        'profile.lastName': 1,
        'profile.avatar': 1
      }
    });
  });

The first autorun gets notifications for a user from the database. The second autorun gets the ids of all users who sent a notification, removes any duplicates and hands it over to a subscription that retrieves the user records for these users.

The if clause was added to prevent unnecessary database queries. It checks if the new set of user ids is identical to the last autorun() and makes the autoron return immediately in this case.

Without this clause, the autorun works as expected. But if I add this if clause, the user collection will be cleared the second time the autorun() is running. It seems as if autorun() automatically unsubscribes from an earlier publication if it is not renewed. Is this true?

It seems to me as though your if statement does the clearing. From what I understand, the first time the autorun runs, it sets lastUserIds to userIds and makes the subscription, but then the second time it runs the autorun function returns, thus not going through with the subscription itself and therefore clears it.

If you want to optimize the reactivity, you could use tools such as

  • computation.firstRun
  • Tracker.afterFlush()
  • callback of the subscription itself (in fact you can carry over your whole second block into the callback of the notifications subscription’s callback)

Furthermore, you should not be needing such a hack to prevent extra database queries. As long as userIds remains the same, Meteor should be clever enough to reuse observers. To make it easier, you can sort the userIds array before handing it down.

Thanks for your detailed reply!

So, if I understand this correctly:

my assumption that the subscription is cleared automatically is correct?

Yes, from the Meteor docs:

If you call Meteor.subscribe within a reactive computation, for example using Tracker.autorun, the subscription will automatically be cancelled when the computation is invalidated or stopped; it’s not necessary to call stop on subscriptions made from inside autorun. However, if the next iteration of your run function subscribes to the same record set (same name and parameters), Meteor is smart enough to skip a wasteful unsubscribe/resubscribe

Interesting, thanks for that citation. Funny thing is that they do not explain that the subscription is discarded if you do not resubscribe. Or is that implied by “when the validation is … stopped”?

I think the return is what’s stopping and therefore causing the implied tearing down of the subscription.

Yep, this may pretty well be the explanation for this phenomenon.

1 Like