From 515174fe2134d7bbe9992a1bb7be4a9e4ef1bdbf Mon Sep 17 00:00:00 2001 From: Brian Broll Date: Thu, 1 Oct 2020 10:34:59 -0500 Subject: [PATCH] Add PromiseEvents. Closes #1939 --- src/common/EventEmitter.js | 4 +++ src/common/PromiseEvents.js | 33 +++++++++++++++++++++++ src/common/compute/interactive/session.js | 29 +++----------------- src/common/utils.js | 5 ++++ test/unit/common/PromiseEvents.spec.js | 26 ++++++++++++++++++ 5 files changed, 72 insertions(+), 25 deletions(-) create mode 100644 src/common/PromiseEvents.js create mode 100644 test/unit/common/PromiseEvents.spec.js diff --git a/src/common/EventEmitter.js b/src/common/EventEmitter.js index 29d48460e..f2ff1b698 100644 --- a/src/common/EventEmitter.js +++ b/src/common/EventEmitter.js @@ -4,6 +4,10 @@ define([ ) { class EventEmitter { constructor() { + this.init(); + } + + init() { this._handlers = {}; } diff --git a/src/common/PromiseEvents.js b/src/common/PromiseEvents.js new file mode 100644 index 000000000..90c7ba611 --- /dev/null +++ b/src/common/PromiseEvents.js @@ -0,0 +1,33 @@ +/*globals define*/ +define([ + 'deepforge/EventEmitter', + 'deepforge/utils', +], function( + EventEmitter, + utils, +) { + class PromiseEvents extends Promise { + constructor(fn) { + super(fn); + this.init(); + } + + static new(fn) { + let promise; + promise = new PromiseEvents(async function(resolve, reject) { + await utils.yield(); + return fn.call(promise, resolve, reject); + }); + return promise; + } + } + + const methods = Object.getOwnPropertyNames(EventEmitter.prototype) + .filter(fn => !PromiseEvents.prototype[fn]); + + methods.forEach(fn => { + PromiseEvents.prototype[fn] = EventEmitter.prototype[fn]; + }); + + return PromiseEvents; +}); diff --git a/src/common/compute/interactive/session.js b/src/common/compute/interactive/session.js index 1f8580efc..d1042c98e 100644 --- a/src/common/compute/interactive/session.js +++ b/src/common/compute/interactive/session.js @@ -1,12 +1,14 @@ /* globals define */ define([ 'deepforge/utils', + 'deepforge/PromiseEvents', 'deepforge/compute/interactive/task', 'deepforge/compute/interactive/message', 'deepforge/compute/interactive/errors', 'deepforge/gmeConfig', ], function( utils, + PromiseEvents, Task, Message, Errors, @@ -167,8 +169,7 @@ define([ const address = gmeConfig.extensions.InteractiveComputeHost || getDefaultServerURL(); - let createSession; - createSession = new PromiseEvents(function(resolve, reject) { + return PromiseEvents.new(function(resolve, reject) { const ws = new WebSocket(address); ws.onopen = () => { ws.send(JSON.stringify([computeID, config, getGMEToken()])); @@ -189,33 +190,11 @@ define([ const err = msg.data; reject(err); } else if (msg.type === Message.STATUS) { - createSession.emit('update', msg.data); + this.emit('update', msg.data); } }; }; }); - - return createSession; - } - } - - class PromiseEvents extends Promise { - constructor(fn) { - super(fn); - this._handlers = {}; - } - - on(event, fn) { - if (!this._handlers[event]) { - this._handlers[event] = []; - } - this._handlers[event].push(fn); - } - - emit(event) { - const handlers = this._handlers[event] || []; - const args = Array.prototype.slice.call(arguments, 1); - handlers.forEach(fn => fn.apply(null, args)); } } diff --git a/src/common/utils.js b/src/common/utils.js index 47f4f31a5..87c5eec1e 100644 --- a/src/common/utils.js +++ b/src/common/utils.js @@ -141,6 +141,10 @@ return deferred.promise; } + function yield() { + return sleep(0); + } + async function waitUntil(fn, interval=50) { while (!await fn()) { await sleep(interval); @@ -167,6 +171,7 @@ withTimeout, defer, sleep, + yield, waitUntil, partition, }; diff --git a/test/unit/common/PromiseEvents.spec.js b/test/unit/common/PromiseEvents.spec.js new file mode 100644 index 000000000..4b8a6bada --- /dev/null +++ b/test/unit/common/PromiseEvents.spec.js @@ -0,0 +1,26 @@ +describe('PromiseEvents', function() { + const assert = require('assert'); + const testFixture = require('../../globals'); + const PromiseEvents = testFixture.requirejs('deepforge/PromiseEvents'); + + it('should resolve as a promise', async function() { + const five = await PromiseEvents.new( + resolve => setTimeout(() => resolve(5), 5) + ); + assert.equal(five, 5); + }); + + it('should support updates', async function() { + const promise = PromiseEvents.new(function(resolve) { + for (let i = 1; i < 6; i++) { + this.emit('update', i); + } + resolve(6); + }); + const events = []; + promise.on('update', status => events.push(status)); + const six = await promise; + assert.equal(events.length, 5); + assert.equal(six, 6); + }); +});