atomicdata-dev/atomic-data-browser

`fetchWebSocket` should return a promise

Opened this issue · 3 comments

Currenlty, fetchWebSocket simply sends a GET message. The received RESOURCE is then properly processed, but the function itself does not return this resource. That means users can't await the requested resource.

How can we fix this?

Well, we could add a .onmessage handler that calls the return thing, which is awaited. Or does this replace the earlier onmessage?

EDIT: Fixed, but we could need a more performant solution (see below)

For this to work the websocket functions have to become stateful. A WebsocketClient class with a map of currently requested resources and corresponding callback should do the trick

I've opted for something like this:

const defaultTimeout = 5000;

/** Sends a GET message for some resource over websockets. */
export async function fetchWebSocket(
  client: WebSocket,
  subject: string,
): Promise<Resource> {
  return new Promise((resolve, reject) => {
    client.addEventListener('message', function listener(ev) {
      const timeoutId = setTimeout(() => {
        client.removeEventListener('message', listener);
        reject(
          new Error(
            `Request for subject "${subject}" timed out after ${defaultTimeout}ms.`,
          ),
        );
      }, defaultTimeout);

      if (ev.data.startsWith('RESOURCE ')) {
        parseResourceMessage(ev).forEach(resource => {
          // if it is the requested subject, return the resource
          if (resource.getSubject() === subject) {
            clearTimeout(timeoutId);
            client.removeEventListener('message', listener);
            resolve(resource);
          }
        });
      }
    });
    client.send('GET ' + subject);
  });
}

I'm having some performance doupts.

Say on init, the user requests about 300 resources using websockets (e.g. when opening a Table or Chat or something).

For each incoming resource, the eventListeners for every other resource are called. This means that we have 300*300 evaluations.

I'm not sure if this will severely impact performance, but I can certainly imagine it will.

One simple way to prevent this, is by only doing the whole eventlistener thing if we know the requesting function is actually waiting for the response. If they aren't awaiting the response, we can simply ignore it.

But how do we know this?

  • In getResourceLoading (the most commonly used fetch-like function), we could pass an option noReturn or something. If this is present, we use a different function, like fetchWebsocketVoid