In my Ephemeral Exchange project, I use socket.io to handle push notifications when any cache values are updated, are deleted or expire. Where you have a lot of asynchronicity going on, it can be hard to deal with all the callbacks.
For example, let’s say that you try to set up a push notification – which then uses (or creates) a socket.io connection, and then set up another before the connection is complete, you would create a new connection unless you are tracking that there is already a connection in progress – but even then how do you communicate back to the second requestor when the first requestor is finished.
All this is made very easy to orchestrate with promises – Here’s how the conversation to asynchronously connect and authenticate with socket.io can be simplified.
The caller
1 2 3 |
if (!sting) { sting = new Socketing (apiEnv); } |
1 |
sting.getConnected(pushId).then (()=> { // do things with the connection }); |
The socketing namespace
Here’s what getConnected looks like in the Socketing namespace – it returns the current promise for connection status, or kicks one off if there isn’t one.
1 |
ns.getConnected = (pushId) => getConnected_ || connect_(pushId); |
1 2 3 4 5 6 7 8 9 10 |
let getConnected_ = null; const connect_ = (pushId) => { // get connected const socket = io.connect(config.socketBase + ":" + config.socketPort); // if a promise, then can be used to handle in progress too. getConnected_ = new Promise((resolve, reject) => { // have the conversation with the server and resolve or reject the promise as required }); return getConnected_; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// deal with the sequnce of connection events connectionEvent() .then(() => passEvent()) .then((passResult) => { if (passResult.ok) { ns.connection.socket = socket; ns.connection.message = passResult; ns.connection.pushId = pushId; resolve(ns); } else { reject('failed to sync passes'); } }) .catch((err)=>reject (err)); |
1 2 3 4 5 6 7 8 9 |
// handle connection event from socket.io function connectionEvent() { return new Promise((resolve, reject) => { // deal with the connection event socket.on('connect', () => resolve()); }); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// handle the password conversation function passEvent() { return new Promise((resolve, reject) => { // the payload to send over var pack = { pass: config.socketPass, id: socket.id, pushId: pushId }; // but only wait a while pTimer_(pack , PASS_TIMEOUT).then (()=>{ if (!ns.isConnected())reject('passevent attempt timed out'); }); // try the conversation socket.emit('pass', pack, (result)=> resolve(result)); }); } |
1 2 3 4 5 |
function pTimer_(id, ms) { return new Promise((resolve, reject) => { setTimeout(() => resolve(id), ms); }); } |
1 2 3 4 5 6 |
// deal with a disconnection event socket.on('disconnect', function(data) { ns.connection.socket = null; ns.connection.message = data; getConnected_ = null; }); |