diff --git a/src/RTMClient.ts b/src/RTMClient.ts index afc366b95..57650a329 100644 --- a/src/RTMClient.ts +++ b/src/RTMClient.ts @@ -131,10 +131,7 @@ export class RTMClient extends EventEmitter { return this.autoReconnect && isRecoverable; }) - .ignore().withAction(() => { - // dispatch 'failure' on parent machine to transition out of this submachine's states - this.stateMachine.handle('failure'); - }) + .transitionTo('failed') .state('authenticated') .onEnter((_state, context) => { this.authenticated = true; @@ -145,6 +142,11 @@ export class RTMClient extends EventEmitter { }) .on('websocket open').transitionTo('handshaking') .state('handshaking') // a state in which to wait until the 'server hello' event + .state('failed') + .onEnter((_state, context) => { + // dispatch 'failure' on parent machine to transition out of this submachine's states + this.stateMachine.handle('failure', context.error); + }) .global() .onStateEnter((state) => { this.logger.debug(`transitioning to state: connecting:${state}`); @@ -231,10 +233,15 @@ export class RTMClient extends EventEmitter { .onSuccess().transitionTo('connecting') .onExit(() => this.teardownWebsocket()) .global() - .onStateEnter((state) => { + .onStateEnter((state, context) => { this.logger.debug(`transitioning to state: ${state}`); - // Emits events: `disconnected`, `connecting`, `connected`, 'disconnecting', 'reconnecting' - this.emit(state); + if (state === 'disconnected') { + // Emits a `disconnected` event with a possible error object (might be undefined) + this.emit(state, context.eventPayload); + } else { + // Emits events: `connecting`, `connected`, `disconnecting`, `reconnecting` + this.emit(state); + } }) .getConfig(); @@ -354,8 +361,7 @@ export class RTMClient extends EventEmitter { * Begin an RTM session using the provided options. This method must be called before any messages can * be sent or received. */ - public start(options?: methods.RTMStartArguments | methods.RTMConnectArguments): void { - // TODO: should this return a Promise? + public start(options?: methods.RTMStartArguments | methods.RTMConnectArguments): Promise { // TODO: make a named interface for the type of `options`. it should end in -Options instead of Arguments. this.logger.debug('start()'); @@ -365,18 +371,40 @@ export class RTMClient extends EventEmitter { // delegate behavior to state machine this.stateMachine.handle('start'); + + // return a promise that resolves with the connection information + return new Promise((resolve, reject) => { + this.once('authenticated', (result) => { + this.removeListener('disconnected', reject); + resolve(result); + }); + this.once('disconnected', (err) => { + this.removeListener('authenticated', resolve); + reject(err); + }); + }); } /** * End an RTM session. After this method is called no messages will be sent or received unless you call * start() again later. */ - public disconnect(): void { - // TODO: should this return a Promise? - this.logger.debug('manual disconnect'); + public disconnect(): Promise { + return new Promise((resolve, reject) => { + this.logger.debug('manual disconnect'); + + // resolve (or reject) on disconnect + this.once('disconnected', (err) => { + if (err instanceof Error) { + reject(err); + } else { + resolve(); + } + }); - // delegate behavior to state machine - this.stateMachine.handle('explicit disconnect'); + // delegate behavior to state machine + this.stateMachine.handle('explicit disconnect'); + }); } /**