aboutsummaryrefslogtreecommitdiff
path: root/src/client/DocServer.ts
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-04-17 09:37:16 -0400
committerbobzel <zzzman@gmail.com>2023-04-17 09:37:16 -0400
commit6a9e80de419af14bece7a48e55edc1543d69f20f (patch)
tree71ae1b819bc4f7fdb699ae90c035eb86275c5006 /src/client/DocServer.ts
parent0a38e3f91f4f85f07fdbb7575ceb678032dcdfe9 (diff)
parent8127616d06b4db2b29de0b13068810fd19e77b5e (diff)
Merge branch 'master' into james-server-stats
Diffstat (limited to 'src/client/DocServer.ts')
-rw-r--r--src/client/DocServer.ts143
1 files changed, 72 insertions, 71 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts
index cab90138f..8f79ebb03 100644
--- a/src/client/DocServer.ts
+++ b/src/client/DocServer.ts
@@ -234,7 +234,7 @@ export namespace DocServer {
* the server if the document has not been cached.
* @param id the id of the requested document
*/
- const _GetRefFieldImpl = async (id: string, force: boolean = false): Promise<Opt<RefField>> => {
+ const _GetRefFieldImpl = (id: string, force: boolean = false): Promise<Opt<RefField>> => {
// an initial pass through the cache to determine whether the document needs to be fetched,
// is already in the process of being fetched or already exists in the
// cache
@@ -309,8 +309,7 @@ export namespace DocServer {
}
export async function getYoutubeChannels() {
- const apiKey = await Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
- return apiKey;
+ return await Utils.EmitCallback(_socket, MessageStore.YoutubeApiQuery, { type: YoutubeQueryTypes.Channels });
}
export function getYoutubeVideos(videoTitle: string, callBack: (videos: any[]) => void) {
@@ -329,10 +328,11 @@ export namespace DocServer {
*/
const _GetRefFieldsImpl = async (ids: string[]): Promise<{ [id: string]: Opt<RefField> }> => {
const requestedIds: string[] = [];
- const waitingIds: string[] = [];
- const promises: Promise<Opt<RefField>>[] = [];
- const map: { [id: string]: Opt<RefField> } = {};
+ const promises: Promise<any>[] = [];
+ let defaultRes: any = undefined;
+ const defaultPromise = new Promise<any>(res => (defaultRes = res));
+ let defaultPromises: { p: Promise<any>; id: string }[] = [];
// 1) an initial pass through the cache to determine
// i) which documents need to be fetched
// ii) which are already in the process of being fetched
@@ -340,6 +340,13 @@ export namespace DocServer {
for (const id of ids) {
const cached = _cache[id];
if (cached === undefined) {
+ defaultPromises.push({
+ id,
+ p: (_cache[id] = new Promise<any>(async res => {
+ await defaultPromise;
+ res(_cache[id]);
+ })),
+ });
// NOT CACHED => we'll have to send a request to the server
requestedIds.push(id);
} else if (cached instanceof Promise) {
@@ -347,10 +354,10 @@ export namespace DocServer {
// and requested one of the documents I'm looking for. Shouldn't fetch again, just
// wait until this promise is resolved (see 7)
promises.push(cached);
- waitingIds.push(id);
+ // waitingIds.push(id);
} else {
// CACHED => great, let's just add it to the field map
- map[id] = cached;
+ // map[id] = cached;
}
}
@@ -358,74 +365,65 @@ export namespace DocServer {
// 2) synchronously, we emit a single callback to the server requesting the serialized (i.e. represented by a string)
// fields for the given ids. This returns a promise, which, when resolved, indicates that all the JSON serialized versions of
// the fields have been returned from the server
- console.log('Requesting ' + requestedIds.length + ' fields');
+ console.log('Requesting ' + requestedIds.length);
FieldLoader.active && runInAction(() => (FieldLoader.ServerLoadStatus.requested = requestedIds.length));
- const getSerializedFields: Promise<any> = Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
+ const serializedFields = await Utils.EmitCallback(_socket, MessageStore.GetRefFields, requestedIds);
// 3) when the serialized RefFields have been received, go head and begin deserializing them into objects.
// Here, once deserialized, we also invoke .proto to 'load' the documents' prototypes, which ensures that all
// future .proto calls on the Doc won't have to go farther than the cache to get their actual value.
-
- let retrieved = 0;
- const fields: { [id: string]: RefField } = {};
- await getSerializedFields.then(async fieldvals => {
- console.log('deserializing ' + fieldvals.length + ' fields');
- const proms: Promise<void>[] = [];
- await runInAction(async () => {
- for (const field of fieldvals) {
- const cached = _cache[field.id];
- if (!cached) {
- retrieved++;
- if (FieldLoader.active && retrieved % 150 === 0) {
- runInAction(() => (FieldLoader.ServerLoadStatus.retrieved = retrieved));
- await new Promise(res => setTimeout(res));
+ let processed = 0;
+ console.log('deserializing ' + serializedFields.length + ' fields');
+ for (const field of serializedFields) {
+ processed++;
+ if (FieldLoader.active && processed % 150 === 0) {
+ runInAction(() => (FieldLoader.ServerLoadStatus.retrieved = processed));
+ await new Promise(res => setTimeout(res)); // force loading to yield to splash screen rendering to update progress
+ }
+ const cached = _cache[field.id];
+ if (!cached || (cached instanceof Promise && defaultPromises.some(dp => dp.p === cached))) {
+ // deserialize
+ // adds to a list of promises that will be awaited asynchronously
+ promises.push(
+ (_cache[field.id] = SerializationHelper.Deserialize(field).then(deserialized => {
+ //overwrite or delete any promises (that we inserted as flags
+ // to indicate that the field was in the process of being fetched). Now everything
+ // should be an actual value within or entirely absent from the cache.
+ if (deserialized !== undefined) {
+ _cache[field.id] = deserialized;
+ } else {
+ delete _cache[field.id];
}
- // deserialize
- const prom = SerializationHelper.Deserialize(field).then(async deserialized => {
- fields[field.id] = deserialized;
-
- //overwrite or delete any promises (that we inserted as flags
- // to indicate that the field was in the process of being fetched). Now everything
- // should be an actual value within or entirely absent from the cache.
- if (deserialized !== undefined) {
- _cache[field.id] = deserialized;
- } else {
- delete _cache[field.id];
- }
- return deserialized;
- });
- // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
- // we set the value at the field's id to a promise that will resolve to the field.
- // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
- // The mapping in the .then call ensures that when other callers await these promises, they'll
- // get the resolved field
- _cache[field.id] = prom;
-
- // adds to a list of promises that will be awaited asynchronously
- proms.push(prom);
- } else if (cached instanceof Promise) {
- console.log('.');
- proms.push(cached as any);
- cached.then((f: any) => (fields[field.id] = f));
- } else if (field) {
- console.log('-');
- proms.push(cached as any);
- fields[field.id] = DocServer.GetCachedRefField(field.id) || field;
- }
- }
- });
- return Promise.all(proms);
- });
+ const promInd = defaultPromises.findIndex(dp => dp.id === field.id);
+ promInd !== -1 && defaultPromises.splice(promInd, 1);
+ return deserialized;
+ }))
+ );
+ // 4) here, for each of the documents we've requested *ourselves* (i.e. weren't promises or found in the cache)
+ // we set the value at the field's id to a promise that will resolve to the field.
+ // When we find that promises exist at keys in the cache, THIS is where they were set, just by some other caller (method).
+ // The mapping in the .then call ensures that when other callers await these promises, they'll
+ // get the resolved field
+ } else if (cached instanceof Promise) {
+ console.log('.');
+ //promises.push(cached);
+ } else if (field) {
+ // console.log('-');
+ }
+ }
+ }
- // 5) at this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose
- // prototype documents, if any, have also been fetched and cached.
- console.log('Deserialized ' + Object.keys(fields).length + ' fields');
+ await Promise.all(promises);
+ defaultPromises.forEach(df => delete _cache[df.id]);
+ defaultRes();
- // 6) with this confidence, we can now go through and update the cache at the ids of the fields that
- // we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given
- // id to the soon-to-be-returned field mapping.
- requestedIds.forEach(id => (map[id] = fields[id]));
- }
+ // 5) at this point, all fields have a) been returned from the server and b) been deserialized into actual Field objects whose
+ // prototype documents, if any, have also been fetched and cached.
+ console.log('Deserialized ' + (requestedIds.length - defaultPromises.length) + ' fields');
+ // 6) with this confidence, we can now go through and update the cache at the ids of the fields that
+ // we explicitly had to fetch. To finish it off, we add whatever value we've come up with for a given
+ // id to the soon-to-be-returned field mapping.
+ //ids.forEach(id => (map[id] = _cache[id] as any));
// 7) those promises we encountered in the else if of 1), which represent
// other callers having already submitted a request to the server for (a) document(s)
@@ -434,16 +432,19 @@ export namespace DocServer {
//
// fortunately, those other callers will also hit their own version of 6) and clean up
// the shared cache when these promises resolve, so all we have to do is...
- const otherCallersFetching = await Promise.all(promises);
+ //const otherCallersFetching = await Promise.all(promises);
// ...extract the RefFields returned from the resolution of those promises and add them to our
// own map.
- waitingIds.forEach((id, index) => (map[id] = otherCallersFetching[index]));
+ // waitingIds.forEach((id, index) => (map[id] = otherCallersFetching[index]));
// now, we return our completed mapping from all of the ids that were passed into the method
// to their actual RefField | undefined values. This return value either becomes the input
// argument to the caller's promise (i.e. GetRefFields(["_id1_", "_id2_", "_id3_"]).then(map => //do something with map...))
// or it is the direct return result if the promise is awaited (i.e. let fields = await GetRefFields(["_id1_", "_id2_", "_id3_"])).
- return map;
+ return ids.reduce((map, id) => {
+ map[id] = _cache[id] as any;
+ return map;
+ }, {} as { [id: string]: Opt<RefField> });
};
let _GetRefFields: (ids: string[]) => Promise<{ [id: string]: Opt<RefField> }> = errorFunc;