JavaScript client for Combo.Socket
Socket Connection
A single connection is established to the server and channels are multiplexed over the connection.
Connect to the server using the Socket class:
let socket = new Socket("/socket", {params: {userToken: "123"}})
socket.connect()
The Socket constructor takes the mount point of the socket,
the authentication params, as well as options that can be found in
the Socket docs, such as configuring the LongPoll transport, and
heartbeat.
Channels
Channels are isolated, concurrent processes on the server that
subscribe to topics and broker events between the client and server.
To join a channel, you must provide the topic, and channel params for
authorization. Here's an example chat room example where "new_msg"
events are listened for, messages are pushed to the server, and
the channel is joined with ok/error/timeout matches:
let channel = socket.channel("room:123", {token: roomToken})
channel.on("new_msg", msg => console.log("Got message", msg) )
$input.onEnter( e => {
channel.push("new_msg", {body: e.target.val}, 10000)
.receive("ok", (msg) => console.log("created message", msg) )
.receive("error", (reasons) => console.log("create failed", reasons) )
.receive("timeout", () => console.log("Networking issue...") )
})
channel.join()
.receive("ok", ({messages}) => console.log("catching up", messages) )
.receive("error", ({reason}) => console.log("failed join", reason) )
.receive("timeout", () => console.log("Networking issue. Still waiting..."))
Joining
Creating a channel with socket.channel(topic, params), binds the params to
channel.params, which are sent up on channel.join().
Subsequent rejoins will send up the modified params for
updating authorization params, or passing up last_message_id information.
Successful joins receive an "ok" status, while unsuccessful joins
receive "error".
With the default serializers and WebSocket transport, JSON text frames are
used for pushing a JSON object literal. If an ArrayBuffer instance is provided,
binary encoding will be used and the message will be sent with the binary
opcode.
Note: binary messages are only supported on the WebSocket transport.
Duplicate Join Subscriptions
While the client may join any number of topics on any number of channels,
the client may only hold a single subscription for each unique topic at any
given time. When attempting to create a duplicate subscription,
the server will close the existing channel, log a warning, and
spawn a new channel for the topic. The client will have their
channel.onClose callbacks fired for the existing channel, and the new
channel join will have its receive hooks processed as normal.
Pushing Messages
From the previous example, we can see that pushing messages to the server
can be done with channel.push(eventName, payload) and we can optionally
receive responses from the push. Additionally, we can use
receive("timeout", callback) to abort waiting for our other receive hooks
and take action after some period of waiting. The default timeout is 10000ms.
Socket Hooks
Lifecycle events of the multiplexed connection can be hooked into via
socket.onError() and socket.onClose() events, ie:
socket.onError( () => console.log("there was an error with the connection!") )
socket.onClose( () => console.log("the connection dropped") )
Channel Hooks
For each joined channel, you can bind to onError and onClose events
to monitor the channel lifecycle, ie:
channel.onError( () => console.log("there was an error!") )
channel.onClose( () => console.log("the channel has gone away gracefully") )
onError hooks
onError hooks are invoked if the socket connection drops, or the channel
crashes on the server. In either case, a channel rejoin is attempted
automatically in an exponential backoff manner.
onClose hooks
onClose hooks are invoked only in two cases. 1) the channel explicitly
closed on the server, or 2). The client explicitly closed, by calling
channel.leave()
Presence
The Presence object provides features for syncing presence information
from the server with the client and handling presences joining and leaving.
Syncing state from the server
To sync presence state from the server, first instantiate an object and pass your channel in to track lifecycle events:
let channel = socket.channel("some:topic")
let presence = new Presence(channel)
Next, use the presence.onSync callback to react to state changes
from the server. For example, to render the list of users every time
the list changes, you could write:
presence.onSync(() => {
myRenderUsersFunction(presence.list())
})
Listing Presences
presence.list is used to return a list of presence information
based on the local state of metadata. By default, all presence
metadata is returned, but a listBy function can be supplied to
allow the client to select which metadata to use for a given presence.
For example, you may have a user online from different devices with
a metadata status of "online", but they have set themselves to "away"
on another device. In this case, the app may choose to use the "away"
status for what appears on the UI. The example below defines a listBy
function which prioritizes the first metadata which was registered for
each user. This could be the first tab they opened, or the first device
they came online from:
let listBy = (id, {metas: [first, ...rest]}) => {
first.count = rest.length + 1 // count of this user's presences
first.id = id
return first
}
let onlineUsers = presence.list(listBy)
Handling individual presence join and leave events
The presence.onJoin and presence.onLeave callbacks can be used to
react to individual presences joining and leaving the app. For example:
let presence = new Presence(channel)
// detect if user has joined for the 1st time or from another tab/device
presence.onJoin((id, current, newPres) => {
if(!current){
console.log("user has entered for the first time", newPres)
} else {
console.log("user additional presence", newPres)
}
})
// detect if user has left from all tabs/devices, or is still present
presence.onLeave((id, current, leftPres) => {
if(current.metas.length === 0){
console.log("user has left from all devices", leftPres)
} else {
console.log("user left from a device", leftPres)
}
})
// receive presence data from server
presence.onSync(() => {
displayUsers(presence.list())
})
- Description:
JavaScript client for Combo.Socket
Socket Connection
A single connection is established to the server and channels are multiplexed over the connection.
Connect to the server using the
Socketclass:let socket = new Socket("/socket", {params: {userToken: "123"}}) socket.connect()The
Socketconstructor takes the mount point of the socket, the authentication params, as well as options that can be found in the Socket docs, such as configuring theLongPolltransport, and heartbeat.Channels
Channels are isolated, concurrent processes on the server that subscribe to topics and broker events between the client and server. To join a channel, you must provide the topic, and channel params for authorization. Here's an example chat room example where
"new_msg"events are listened for, messages are pushed to the server, and the channel is joined with ok/error/timeout matches:let channel = socket.channel("room:123", {token: roomToken}) channel.on("new_msg", msg => console.log("Got message", msg) ) $input.onEnter( e => { channel.push("new_msg", {body: e.target.val}, 10000) .receive("ok", (msg) => console.log("created message", msg) ) .receive("error", (reasons) => console.log("create failed", reasons) ) .receive("timeout", () => console.log("Networking issue...") ) }) channel.join() .receive("ok", ({messages}) => console.log("catching up", messages) ) .receive("error", ({reason}) => console.log("failed join", reason) ) .receive("timeout", () => console.log("Networking issue. Still waiting..."))Joining
Creating a channel with
socket.channel(topic, params), binds the params tochannel.params, which are sent up onchannel.join(). Subsequent rejoins will send up the modified params for updating authorization params, or passing up last_message_id information. Successful joins receive an "ok" status, while unsuccessful joins receive "error".With the default serializers and WebSocket transport, JSON text frames are used for pushing a JSON object literal. If an
ArrayBufferinstance is provided, binary encoding will be used and the message will be sent with the binary opcode.Note: binary messages are only supported on the WebSocket transport.
Duplicate Join Subscriptions
While the client may join any number of topics on any number of channels, the client may only hold a single subscription for each unique topic at any given time. When attempting to create a duplicate subscription, the server will close the existing channel, log a warning, and spawn a new channel for the topic. The client will have their
channel.onClosecallbacks fired for the existing channel, and the new channel join will have its receive hooks processed as normal.Pushing Messages
From the previous example, we can see that pushing messages to the server can be done with
channel.push(eventName, payload)and we can optionally receive responses from the push. Additionally, we can usereceive("timeout", callback)to abort waiting for our otherreceivehooks and take action after some period of waiting. The default timeout is 10000ms.Socket Hooks
Lifecycle events of the multiplexed connection can be hooked into via
socket.onError()andsocket.onClose()events, ie:socket.onError( () => console.log("there was an error with the connection!") ) socket.onClose( () => console.log("the connection dropped") )Channel Hooks
For each joined channel, you can bind to
onErrorandonCloseevents to monitor the channel lifecycle, ie:channel.onError( () => console.log("there was an error!") ) channel.onClose( () => console.log("the channel has gone away gracefully") )onError hooks
onErrorhooks are invoked if the socket connection drops, or the channel crashes on the server. In either case, a channel rejoin is attempted automatically in an exponential backoff manner.onClose hooks
onClosehooks are invoked only in two cases. 1) the channel explicitly closed on the server, or 2). The client explicitly closed, by callingchannel.leave()Presence
The
Presenceobject provides features for syncing presence information from the server with the client and handling presences joining and leaving.Syncing state from the server
To sync presence state from the server, first instantiate an object and pass your channel in to track lifecycle events:
let channel = socket.channel("some:topic") let presence = new Presence(channel)Next, use the
presence.onSynccallback to react to state changes from the server. For example, to render the list of users every time the list changes, you could write:presence.onSync(() => { myRenderUsersFunction(presence.list()) })Listing Presences
presence.listis used to return a list of presence information based on the local state of metadata. By default, all presence metadata is returned, but alistByfunction can be supplied to allow the client to select which metadata to use for a given presence. For example, you may have a user online from different devices with a metadata status of "online", but they have set themselves to "away" on another device. In this case, the app may choose to use the "away" status for what appears on the UI. The example below defines alistByfunction which prioritizes the first metadata which was registered for each user. This could be the first tab they opened, or the first device they came online from:let listBy = (id, {metas: [first, ...rest]}) => { first.count = rest.length + 1 // count of this user's presences first.id = id return first } let onlineUsers = presence.list(listBy)Handling individual presence join and leave events
The
presence.onJoinandpresence.onLeavecallbacks can be used to react to individual presences joining and leaving the app. For example:let presence = new Presence(channel) // detect if user has joined for the 1st time or from another tab/device presence.onJoin((id, current, newPres) => { if(!current){ console.log("user has entered for the first time", newPres) } else { console.log("user additional presence", newPres) } }) // detect if user has left from all tabs/devices, or is still present presence.onLeave((id, current, leftPres) => { if(current.metas.length === 0){ console.log("user has left from all devices", leftPres) } else { console.log("user left from a device", leftPres) } }) // receive presence data from server presence.onSync(() => { displayUsers(presence.list()) })
- Source: