user15254140
user15254140

Reputation:

how does svelte unsubscribe actually work?

I can subscribe to a store like this:

count.subscribe(value => {
  count_value = value;
});

but when we want to unsubscribe we will put the previous subscribe code into a new variable (becomes a function expression) and run it only when the component is destroyed (onDestroy)

const unsubscribe = count.subscribe(value => {
    count_value = value;
});

onDestroy(unsubscribe);

the question is, how about just putting the previous function into a new variable called unsubscribe. can perform the unsubscribe function to the store. I mean we don't even change the subscribe code at all for the unsubscribe implementation, all we do is put it in a new variable so it becomes a function expression and only call it via onDestroy, then how can it magically unsubscribe? how does it actually work?

Upvotes: 4

Views: 2904

Answers (2)

johannchopin
johannchopin

Reputation: 14873

but when we want to unsubscribe we will put the previous subscribe code into a new variable

That's not what is happening here. The function is not assigned to the unsubscribe variable; its return value is another function.

Imagine subscribe as something like this:

const subscribe = (callback) => {
  callback('the stored value')

  const unsubscribe = () => {
    // do all the stuffs to unsubscribe here...
    console.log('Your are unsubscribed!')
  }

  return unsubscribe
}

let count_value

// unsubscribe is now the function returned in the subscribe function
const unsubscribe = subscribe(value => {
  count_value = value;
})

console.log(count_value)

// calling this function will log 'You are unsubscribed!' in the console
unsubscribe()

Upvotes: 2

Connor Low
Connor Low

Reputation: 7236

subscribe does not return the function you passed as a parameter: it returns a new function that implements the unsubscribe behavior.


This is easier to understand if you look at the Svelte documentation for custom store contracts (emphasis added):

Store contract

store = { subscribe: (subscription: (value: any) => void) => (() => void), set?: (value: any) => void }

You can create your own stores without relying on svelte/store, by implementing the store contract:

  1. A store must contain a .subscribe method, which must accept as its argument a subscription function. This subscription function must be immediately and synchronously called with the store's current value upon calling .subscribe. All of a store's active subscription functions must later be synchronously called whenever the store's value changes.
  2. The .subscribe method must return an unsubscribe function. Calling an unsubscribe function must stop its subscription, and its corresponding subscription function must not be called again by the store.

If you want further proof that the return value of subscribe is different than the function you passed it, just extract the type for store.subscribe:

Type subscribe = (subscription: (value: any) => void) => (() => void)

The signature of the subscription parameter, (value: any) => void, does not match the signature of the return value, () => void.


Here is a simple implementation of the store contract demonstrating how the unsubscribe function is built.

class Store {
    constructor(init) {
        this.subscribers = {};
        this.value = init;
    }
    subscribe(callback) {
        callback(this.value);
        const id = Symbol()
        this.subscribers[id] = callback;

        return () => delete this.subscribers[id]
        //     ^^^ unsubscribe function here ^^^

    }
    set(value) {
        this.value = value;
        for (const id of Object.getOwnPropertySymbols(this.subscribers)) {
            this.subscribers[id](value)
        }
    }
}

Upvotes: 8

Related Questions