How do you implement your own listenable in Verse?

I might have found a better way

custom_cancelable<public> := class:
    var Canceled : logic = false

    CancelEvent : event() = event(){}

    Cancel():void=
        set Canceled = true
        CancelEvent.Signal()

custom_subscribable<public>(t:type)<computes> := class<public>(awaitable(t)):

    InnerEvent : event(t) = event(t){}

    Subscribe(Callback:type {__(:t):void}):custom_cancelable=
        Cancelable := custom_cancelable{}
        spawn{_WaitForEvent(Callback, Cancelable)}

        Cancelable

    _WaitForEvent(Callback:type {__(:t):void}, Cancelable: custom_cancelable)<suspends>:void=
        race:
            # Wait for payload to be sent
            loop:
                Payload := InnerEvent.Await()

                # Just in case
                if(not Cancelable.Canceled?):
                    Callback(Payload)
                else:
                    break # Just in case

            # Cancelable got canceled
            Cancelable.CancelEvent.Await()

    
    Signal<public>(Payload:t):void =
        InnerEvent.Signal(Payload)

    Await<override>()<suspends>:t=
        InnerEvent.Await()

These might not be stored along in an array of cancelable, but it seem to work good (forgetting that it uses spawns)

I’m not sure that it works flawlessly for now, so if someone wants to try it out :person_shrugging:

EDIT: For anyone wanting to use this, just need to warn you, the garbage collector will not clean up the suspending calls (which is obvious but I didn’t realise at the time)
So each .Subscribe() call will stay alive untill you manually call .Cancel() or .CancelAll()
So to say, try to not dynamically generate .Subscribe() calls and you should be fine :+1:

7 Likes