Skip to content

Instantly share code, notes, and snippets.

@fijiwebdesign
Last active April 15, 2018 20:24
Show Gist options
  • Save fijiwebdesign/3b7ffeea0734dc3314bcbe9fdd6b543f to your computer and use it in GitHub Desktop.
Save fijiwebdesign/3b7ffeea0734dc3314bcbe9fdd6b543f to your computer and use it in GitHub Desktop.
Emit promises in place of events
const { EventEmitter } = require('events');
const PromiseEmitter = require('../PromiseEmitter');
const eventEmitter = new EventEmitter();
const promiseEmitter = new PromiseEmitter(eventEmitter);
// simulate events
setTimeout(() => eventEmitter.emit('foo', { name: 'foo', age: 'unknown' }));
setTimeout(() => eventEmitter.emit('bar', { name: 'bar', age: '5 ses' }), 5000);
promiseEmitter
.on('foo')
.timeout(1000) // timeout after 1 sec
.then(foo => console.log('foo event', foo))
.catch(err => console.log('foo event error', err));
promiseEmitter
.on('bar')
.timeout(1000) // timeout after 1 sec
.then(foo => console.log('bar event', foo))
.catch(err => console.log('bar event error', err));
/**
* Emit a promise once in place of event with timeout
* @param {EventEmitter} emitter
*/
function PromiseEmitter(emitter) {
this.emitter = emitter;
}
PromiseEmitter.prototype.once = function once(event) {
let timer;
const promise = new Promise(resolve => {
this.emitter.once(event, (...args) => {
resolve.apply(this.emitter, args);
});
});
promise.timeout = timeout => {
const timeoutErr = new Error('Timeout');
clearTimeout(timer);
return new Promise((resolve, reject) => {
setTimeout(() => reject.call(this.emitter, timeoutErr), timeout);
promise.then((...args) => resolve.apply(this.emitter, args));
});
};
return promise;
};
PromiseEmitter.prototype.on = PromiseEmitter.prototype.once;
module.exports = PromiseEmitter;
const { EventEmitter } = require('events');
const PromiseEmitter = require('../lib/PromiseEmitter');
// const io = require('socket.io/client');
// const socket = io.connect('http://localhost'); // socket is an EventEmitter
const socket = new EventEmitter(); // mock socket.io
socket.promise = new PromiseEmitter(socket); // wrap on(event) with promise
// emulate socket.io
setImmediate(() => socket.emit('connected'));
function onceLoggedIn(status) {
console.log('auth status', status);
}
function onceLoginError(err) {
console.log('auth err', err);
}
function showRooms(rooms) {
console.log('rooms', rooms);
}
function showRoomUsers({ room, users }) {
console.log('show room %o users %o ', room, users);
}
// mix events and promises to for async, parallel, sync, sequential flow control
socket.on('connected', () => {
setImmediate(() => socket.emit('auth', { username: 'foo', password: 'bar' }));
socket.promise
.once('auth')
.timeout(1000) // timeout after 1 sec
.then(status => onceLoggedIn(status))
.then(() => {
setImmediate(() => socket.emit('rooms/list', [{ id: 1 }, { id: 2 }])); // get rooms list
return socket.promise.once('rooms/list');
})
.then(rooms => {
// generate an async event for each room
showRooms(rooms);
return rooms.map(room => {
const emitter = new EventEmitter();
setTimeout(() => emitter.emit('click', room), Math.random() * 10000); // simulate UI room click
return emitter;
});
})
.then(emitters => {
// many async events into one promise
const promises = emitters.map(emitter => new PromiseEmitter(emitter).once('click'));
const users = [{ name: 'joe' }, { name: 'jane' }];
return Promise.race(promises).then(room => {
console.log('Emitted click on', room);
setImmediate(() => socket.emit('room/users', users)); // get users in room
return socket.promise
.once('room/users')
.then(roomUsers => Promise.resolve({ users: roomUsers, room }));
});
})
.then(room => {
showRoomUsers(room);
})
.catch(err => onceLoginError(err));
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment