diff --git a/src/plugins/liveobjects/liveobjects.ts b/src/plugins/liveobjects/liveobjects.ts index 127980308..57212e661 100644 --- a/src/plugins/liveobjects/liveobjects.ts +++ b/src/plugins/liveobjects/liveobjects.ts @@ -2,6 +2,7 @@ import type BaseClient from 'common/lib/client/baseclient'; import type RealtimeChannel from 'common/lib/client/realtimechannel'; import type ErrorInfo from 'common/lib/types/errorinfo'; import type * as API from '../../../ably'; +import type EventEmitter from 'common/lib/util/eventemitter'; import { LiveCounter } from './livecounter'; import { LiveMap } from './livemap'; import { LiveObject } from './liveobject'; @@ -9,9 +10,16 @@ import { LiveObjectsPool, ROOT_OBJECT_ID } from './liveobjectspool'; import { StateMessage } from './statemessage'; import { SyncLiveObjectsDataPool } from './syncliveobjectsdatapool'; +enum LiveObjectsEvents { + SyncCompleted = 'SyncCompleted', +} + export class LiveObjects { private _client: BaseClient; private _channel: RealtimeChannel; + // composition over inheritance since we cannot import class directly into plugin code. + // instead we obtain a class type from the client + private _eventEmitter: EventEmitter; private _liveObjectsPool: LiveObjectsPool; private _syncLiveObjectsDataPool: SyncLiveObjectsDataPool; private _syncInProgress: boolean; @@ -21,14 +29,24 @@ export class LiveObjects { constructor(channel: RealtimeChannel) { this._channel = channel; this._client = channel.client; + this._eventEmitter = new this._client.EventEmitter(this._client.logger); this._liveObjectsPool = new LiveObjectsPool(this); this._syncLiveObjectsDataPool = new SyncLiveObjectsDataPool(this); this._syncInProgress = true; } async getRoot(): Promise { - // TODO: wait for SYNC sequence to finish to return root - return this._liveObjectsPool.get(ROOT_OBJECT_ID) as LiveMap; + if (!this._syncInProgress) { + // SYNC is finished, can return immediately root object from pool + return this._liveObjectsPool.get(ROOT_OBJECT_ID) as LiveMap; + } + + // otherwise wait for SYNC sequence to finish + return new Promise((res) => { + this._eventEmitter.once(LiveObjectsEvents.SyncCompleted, () => { + res(this._liveObjectsPool.get(ROOT_OBJECT_ID) as LiveMap); + }); + }); } /** diff --git a/test/realtime/live_objects.test.js b/test/realtime/live_objects.test.js index 81140a333..d329bcf87 100644 --- a/test/realtime/live_objects.test.js +++ b/test/realtime/live_objects.test.js @@ -57,6 +57,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'live_objects'], function ( await helper.monitorConnectionThenCloseAndFinish(async () => { const channel = client.channels.get('channel'); const liveObjects = channel.liveObjects; + await channel.attach(); const root = await liveObjects.getRoot(); expect(root.constructor.name).to.equal('LiveMap'); @@ -71,6 +72,7 @@ define(['ably', 'shared_helper', 'async', 'chai', 'live_objects'], function ( await helper.monitorConnectionThenCloseAndFinish(async () => { const channel = client.channels.get('channel'); const liveObjects = channel.liveObjects; + await channel.attach(); const root = await liveObjects.getRoot(); helper.recordPrivateApi('call.LiveObject.getObjectId');