Skip to content

Commit 6f283d5

Browse files
Tree-shakeable FirestoreClient (#3492)
1 parent fc71940 commit 6f283d5

File tree

1 file changed

+157
-67
lines changed

1 file changed

+157
-67
lines changed

packages/firestore/src/core/firestore_client.ts

Lines changed: 157 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ import { Query } from './query';
4242
import { Transaction } from './transaction';
4343
import { ViewSnapshot } from './view_snapshot';
4444
import {
45-
OnlineComponentProvider,
4645
MemoryOfflineComponentProvider,
47-
OfflineComponentProvider
46+
OfflineComponentProvider,
47+
OnlineComponentProvider
4848
} from './component_provider';
49+
import { PartialObserver, Unsubscribe } from '../api/observer';
4950
import { AsyncObserver } from '../util/async_observer';
5051

5152
const LOG_TAG = 'FirestoreClient';
@@ -92,6 +93,15 @@ export class FirestoreClient {
9293

9394
private readonly clientId = AutoId.newId();
9495

96+
// We defer our initialization until we get the current user from
97+
// setChangeListener(). We block the async queue until we got the initial
98+
// user and the initialization is completed. This will prevent any scheduled
99+
// work from happening before initialization is completed.
100+
//
101+
// If initializationDone resolved then the FirestoreClient is in a usable
102+
// state.
103+
private readonly initializationDone = new Deferred<void>();
104+
95105
constructor(
96106
private credentials: CredentialsProvider,
97107
/**
@@ -155,15 +165,6 @@ export class FirestoreClient {
155165

156166
this.databaseInfo = databaseInfo;
157167

158-
// We defer our initialization until we get the current user from
159-
// setChangeListener(). We block the async queue until we got the initial
160-
// user and the initialization is completed. This will prevent any scheduled
161-
// work from happening before initialization is completed.
162-
//
163-
// If initializationDone resolved then the FirestoreClient is in a usable
164-
// state.
165-
const initializationDone = new Deferred<void>();
166-
167168
// If usePersistence is true, certain classes of errors while starting are
168169
// recoverable but only by falling back to persistence disabled.
169170
//
@@ -185,7 +186,7 @@ export class FirestoreClient {
185186
persistenceSettings,
186187
user,
187188
persistenceResult
188-
).then(initializationDone.resolve, initializationDone.reject);
189+
).then(this.initializationDone.resolve, this.initializationDone.reject);
189190
} else {
190191
this.asyncQueue.enqueueRetryable(() =>
191192
this.remoteStore.handleCredentialChange(user)
@@ -194,9 +195,7 @@ export class FirestoreClient {
194195
});
195196

196197
// Block the async queue until initialization is done
197-
this.asyncQueue.enqueueAndForget(() => {
198-
return initializationDone.promise;
199-
});
198+
this.asyncQueue.enqueueAndForget(() => this.initializationDone.promise);
200199

201200
// Return only the result of enabling persistence. Note that this does not
202201
// need to await the completion of initializationDone because the result of
@@ -408,62 +407,22 @@ export class FirestoreClient {
408407
docKey: DocumentKey
409408
): Promise<Document | null> {
410409
this.verifyNotTerminated();
411-
const deferred = new Deferred<Document | null>();
412-
await this.asyncQueue.enqueue(async () => {
413-
try {
414-
const maybeDoc = await this.localStore.readDocument(docKey);
415-
if (maybeDoc instanceof Document) {
416-
deferred.resolve(maybeDoc);
417-
} else if (maybeDoc instanceof NoDocument) {
418-
deferred.resolve(null);
419-
} else {
420-
deferred.reject(
421-
new FirestoreError(
422-
Code.UNAVAILABLE,
423-
'Failed to get document from cache. (However, this document may ' +
424-
"exist on the server. Run again without setting 'source' in " +
425-
'the GetOptions to attempt to retrieve the document from the ' +
426-
'server.)'
427-
)
428-
);
429-
}
430-
} catch (e) {
431-
const firestoreError = wrapInUserErrorIfRecoverable(
432-
e,
433-
`Failed to get document '${docKey} from cache`
434-
);
435-
deferred.reject(firestoreError);
436-
}
437-
});
438-
439-
return deferred.promise;
410+
await this.initializationDone.promise;
411+
return enqueueReadDocumentFromCache(
412+
this.asyncQueue,
413+
this.localStore,
414+
docKey
415+
);
440416
}
441417

442418
async getDocumentsFromLocalCache(query: Query): Promise<ViewSnapshot> {
443419
this.verifyNotTerminated();
444-
const deferred = new Deferred<ViewSnapshot>();
445-
await this.asyncQueue.enqueue(async () => {
446-
try {
447-
const queryResult = await this.localStore.executeQuery(
448-
query,
449-
/* usePreviousResults= */ true
450-
);
451-
const view = new View(query, queryResult.remoteKeys);
452-
const viewDocChanges = view.computeDocChanges(queryResult.documents);
453-
const viewChange = view.applyChanges(
454-
viewDocChanges,
455-
/* updateLimboDocuments= */ false
456-
);
457-
deferred.resolve(viewChange.snapshot!);
458-
} catch (e) {
459-
const firestoreError = wrapInUserErrorIfRecoverable(
460-
e,
461-
`Failed to execute query '${query} against cache`
462-
);
463-
deferred.reject(firestoreError);
464-
}
465-
});
466-
return deferred.promise;
420+
await this.initializationDone.promise;
421+
return enqueueExecuteQueryFromCache(
422+
this.asyncQueue,
423+
this.localStore,
424+
query
425+
);
467426
}
468427

469428
write(mutations: Mutation[]): Promise<void> {
@@ -512,3 +471,134 @@ export class FirestoreClient {
512471
return deferred.promise;
513472
}
514473
}
474+
475+
export function enqueueWrite(
476+
asyncQueue: AsyncQueue,
477+
syncEngine: SyncEngine,
478+
mutations: Mutation[]
479+
): Promise<void> {
480+
const deferred = new Deferred<void>();
481+
asyncQueue.enqueueAndForget(() => syncEngine.write(mutations, deferred));
482+
return deferred.promise;
483+
}
484+
485+
export function enqueueNetworkEnabled(
486+
asyncQueue: AsyncQueue,
487+
remoteStore: RemoteStore,
488+
persistence: Persistence,
489+
enabled: boolean
490+
): Promise<void> {
491+
return asyncQueue.enqueue(() => {
492+
persistence.setNetworkEnabled(enabled);
493+
return enabled ? remoteStore.enableNetwork() : remoteStore.disableNetwork();
494+
});
495+
}
496+
497+
export function enqueueWaitForPendingWrites(
498+
asyncQueue: AsyncQueue,
499+
syncEngine: SyncEngine
500+
): Promise<void> {
501+
const deferred = new Deferred<void>();
502+
asyncQueue.enqueueAndForget(() => {
503+
return syncEngine.registerPendingWritesCallback(deferred);
504+
});
505+
return deferred.promise;
506+
}
507+
508+
export function enqueueListen(
509+
asyncQueue: AsyncQueue,
510+
eventManger: EventManager,
511+
query: Query,
512+
options: ListenOptions,
513+
observer: PartialObserver<ViewSnapshot>
514+
): Unsubscribe {
515+
const wrappedObserver = new AsyncObserver(observer);
516+
const listener = new QueryListener(query, wrappedObserver, options);
517+
asyncQueue.enqueueAndForget(() => eventManger.listen(listener));
518+
return () => {
519+
wrappedObserver.mute();
520+
asyncQueue.enqueueAndForget(() => eventManger.unlisten(listener));
521+
};
522+
}
523+
524+
export function enqueueSnapshotsInSyncListen(
525+
asyncQueue: AsyncQueue,
526+
eventManager: EventManager,
527+
observer: PartialObserver<void>
528+
): Unsubscribe {
529+
const wrappedObserver = new AsyncObserver(observer);
530+
asyncQueue.enqueueAndForget(async () =>
531+
eventManager.addSnapshotsInSyncListener(wrappedObserver)
532+
);
533+
return () => {
534+
wrappedObserver.mute();
535+
asyncQueue.enqueueAndForget(async () =>
536+
eventManager.removeSnapshotsInSyncListener(wrappedObserver)
537+
);
538+
};
539+
}
540+
541+
export async function enqueueReadDocumentFromCache(
542+
asyncQueue: AsyncQueue,
543+
localStore: LocalStore,
544+
docKey: DocumentKey
545+
): Promise<Document | null> {
546+
const deferred = new Deferred<Document | null>();
547+
await asyncQueue.enqueue(async () => {
548+
try {
549+
const maybeDoc = await localStore.readDocument(docKey);
550+
if (maybeDoc instanceof Document) {
551+
deferred.resolve(maybeDoc);
552+
} else if (maybeDoc instanceof NoDocument) {
553+
deferred.resolve(null);
554+
} else {
555+
deferred.reject(
556+
new FirestoreError(
557+
Code.UNAVAILABLE,
558+
'Failed to get document from cache. (However, this document may ' +
559+
"exist on the server. Run again without setting 'source' in " +
560+
'the GetOptions to attempt to retrieve the document from the ' +
561+
'server.)'
562+
)
563+
);
564+
}
565+
} catch (e) {
566+
const firestoreError = wrapInUserErrorIfRecoverable(
567+
e,
568+
`Failed to get document '${docKey} from cache`
569+
);
570+
deferred.reject(firestoreError);
571+
}
572+
});
573+
return deferred.promise;
574+
}
575+
576+
export async function enqueueExecuteQueryFromCache(
577+
asyncQueue: AsyncQueue,
578+
localStore: LocalStore,
579+
query: Query
580+
): Promise<ViewSnapshot> {
581+
const deferred = new Deferred<ViewSnapshot>();
582+
await asyncQueue.enqueue(async () => {
583+
try {
584+
const queryResult = await localStore.executeQuery(
585+
query,
586+
/* usePreviousResults= */ true
587+
);
588+
const view = new View(query, queryResult.remoteKeys);
589+
const viewDocChanges = view.computeDocChanges(queryResult.documents);
590+
const viewChange = view.applyChanges(
591+
viewDocChanges,
592+
/* updateLimboDocuments= */ false
593+
);
594+
deferred.resolve(viewChange.snapshot!);
595+
} catch (e) {
596+
const firestoreError = wrapInUserErrorIfRecoverable(
597+
e,
598+
`Failed to execute query '${query} against cache`
599+
);
600+
deferred.reject(firestoreError);
601+
}
602+
});
603+
return deferred.promise;
604+
}

0 commit comments

Comments
 (0)