@swnb/event
v0.18.0
Published
event module
Readme
@swnb/event
pub/sub module implement with typescript
install
npm install @swnb/event
# or
yarn add @swnb/event
# or
pnpm i @swnb/eventwhy we need another event module
three reason why you should use this module
1. simplify event binding
in react , you have to write code like this to bind event
useEffect(() => {
const callback = () => {}
target.addEventListener("event", callback)
return () => {
target.removeEventListener("event", callback)
}
}, [target])if you want to bind multiple events, you will have to do this
useEffect(() => {
const callback1 = () => {}
target.addEventListener("event1", callback1)
const callback2 = () => {}
target.addEventListener("event2", callback2)
// ....
return () => {
target.removeEventListener("event1", callback1)
target.removeEventListener("event2", callback2)
// ....
}
}, [target])when you use @swnb/event
import { EventProxy } from "@swnb/event"
useEffect(
() =>
EventProxy.new(target)
.on("event1", (...args) => {}) // support type hint !
.on("event2", (...args) => {}) // support type hint !
.on("event3", (...args) => {}),// support type hint !
[target]
)programming has never been easier
2. Promise support
consider a scenario where you want to set up a 'websocket' connection, and wait for the connection to open, and set the maximum connection duration, consider the correct release of resources, you might write the following code
async function connect(url: string, timeout: number) {
const ws = new WebSocket(url)
return new Promise<WebSocket>((res, rej) => {
const timeID = setTimeout(() => {
rej(new Error("timeout"))
ws.removeEventListener("open", onOpen)
}, timeout)
function onOpen() {
res(ws)
clearTimeout(timeID)
ws.removeEventListener("open", onOpen)
}
ws.addEventListener("open", onOpen)
})
}when you use @swnb/event
import { EventProxy } from "@swnb/event"
async function connect(url: string, timeout: number) {
const ws = new WebSocket(url)
await EventProxy.new(ws).waitUtil("open", { timeout }) // support type hint !
return ws
}consider a more complex scenario where you create a webrtc connection and wait for the connection to 'connected'
import { EventProxy } from "@swnb/event"
async function connect(timeout: number) {
const connection = new RTCPeerConnection()
await EventProxy.new(connection).waitUtil(
"connectionstatechange",
{
timeout,
where: (ev) => connection.connectionState === "connected",
}
)
return connection
}use 'where' to select the 'connectionState' you want
Observe all the events of a web object
if you want to know what events are fired when 'video' is played, consider writing this
import { EventProxy } from "@swnb/event"
// support type hint !
EventProxy.new(videoDom, { proxyAllEvent: true }).any((eventName, ...args) => {
console.log(eventName)
})in react
import { EventProxy } from "@swnb/event"
import { useEffect, useRef } from "react"
function Video() {
const videoDomRef = useRef<HTMLVideoElement>(null)
useEffect(() => {
return EventProxy.new(videoDomRef.current!).any(
(eventName, ...args) => {
console.log(eventName)
}
)
}, [])
const url = "" // your video link
return <video muted autoPlay src={url} ref={videoDomRef} />
}open the console and you will see the order and time of all the 'video' events

concept
there are two main class in @swnb/event, SyncEvent, EventProxy
SyncEvent
define event handler map
import { SyncEvent } from '@swnb/event'
// define event handler type
type EventHandlerMap = {
ev1: (arg1: string, arg2:number) => void
ev2: (arg1: boolean) => void
}
create instance
const eventBus = new SyncEvent<EventHandlerMap>()
// or
const eventBus = SyncEvent.new<EventHandlerMap>()register event callback
// type hint support!
eventBus.on('ev1', (arg1,arg2) => {
})
eventBus.once('ev2',arg1 =>{
// this callback only execute one time
})register multiple event
// type hint support!
eventBus
.on('ev1', (arg1,arg2) => {
})
.once('ev2',arg1 =>{
})
.on("ev1",(arg1,arg2)=>{
})emit event with argument
eventBus.emit('ev1', "1", 2)cancel register callback
const callback = (arg1: boolean) => {}
const cancelFn = eventBus.on('ev2', callback)
// cancel register
eventBus.off('ev2', callback)
// or
cancelFn()
eventBus.offAll() // cancel all register callback
eventBus.offAll('ev2') // only cancel event type bar callbackcancel multiple register at once
const cancelFn = eventBus.on('ev2', ()=>{
}).on('ev1',()=>{
})
// clear previous two registered functions at once
cancelFn()use method waitUtil instead of method on
// type hint support
const [arg1, arg2] = await eventBus.waitUtil('ev1') // this code block util 'ev1' emit ,set timeout for waitUtil
const [arg1, arg2] = await eventBus.waitUtil('ev1',{ timeout:1000 }) // after one second, this method will throw timeout errorcancel waitUtil
async function main() {
const cancelRef = { current() {} };
setTimeout(()=>{
cancelRef.current()
},1000)
await eventBus.waitUtil('ev1',{ cancelRef }) // throw cancel error after one second
}select condition
await eventBus.waitUtil('ev1',{ where(arg1,arg2){
return arg2 > 1000
}})
// waitUtil resolve only if arg2 > 1000EventProxy
EventProxy has all the functionality of SyncEvent
on top of that, you can use it to bind some web objects, in react you can do that with no performance cost
import { EventProxy } from "@swnb/event"
useEffect(() => {
return EventProxy.new(window)
.on("click", (ev) => {}) // support type hint
.on("resize", (ev) => {})
.on("contextmenu", (ev) => {})
}, [])if you want to watch all event for element , set options proxyAllEvent to true to registers all events, you needs to call destroy to clean up all registered events otherwise it will cause memory leak
use EventProxy to observe all events triggered in the internal video
initializing
EventProxywithproxyAllEventis not that cheap, it's better to cache it
import { EventProxy } from "@swnb/event"
import { useEffect, useRef } from "react"
function Video() {
const videoDomRef = useRef<HTMLVideoElement>(null)
useEffect(() => {
const eventProxy = EventProxy.new(videoDomRef.current!, { proxyAllEvent: true }).any(
(eventName, ...args) => {
console.log(eventName)
}
)
return eventProxy.destroy
}, [])
const url = "" // your video link
return <video muted autoPlay src={url} ref={videoDomRef} />
}to be continue
