deepstream.io-client-js icon indicating copy to clipboard operation
deepstream.io-client-js copied to clipboard

record.whenReady triggers when document isn't ready yet

Open alanhoff opened this issue 4 years ago • 3 comments

Versions:

  • npm: @deepstream/client v4.1.3
  • docker: deepstreamio/deepstream.io:latest

In this test the hello-world record contains a simple object: {hello: 'world'}. The test file:

const deepstream = require('@deepstream/client');
const client = deepstream('ws://localhost:6020');

client.login();

const record = client.record.getRecord('hello-world');

record.whenReady(() => {
  console.log('event: whenReady', `ready: ${record.isReady}`, JSON.stringify(record.get()));
});

record.subscribe(() => {
  console.log('event: subscribe', `ready: ${record.isReady}`, JSON.stringify(record.get()));
});

What I expected to see in my console is the following:

event: subscribe ready: false {}
event: whenReady ready: true {"hello": "world"}
event: subscribe ready: true {"hello": "world"}

But what I get is this:

Screen Shot 2019-09-11 at 11 36 52

The first log shows that record.isReady is true when it's clearly not. The second log is triggered by whenReady but as you can see the document is still empty so it's not actually ready. The third log is a total mystery why it happens because nothing has changed since the first subscribe event. Only in the fourth log the record contains all the data from the server.

Question: How do I know when the record is truly ready?

alanhoff avatar Sep 11 '19 14:09 alanhoff

What I'm doing for now is this "hack":

const deepstream = require("@deepstream/client");
const client = deepstream("ws://localhost:6020");

const stateMatchGroups = [
  ["MERGING", "READY"], // When using `record.getRecord` before being logged
  ["SUBSCRIBING", "READY"], // When `record.getRecord` yields an empty document
  ["RESUBSCRIBING", "READY"] // When using `record.getRecord` after being logged
];

function isActuallyReady(record) {
  return record.record.stateMachine.history.some(state =>
    stateMatchGroups.some(
      ([oldState, newState]) =>
        state.oldState === oldState && state.newState === newState
    )
  );
}

function whenActuallyReady(record, callback) {
  const listener = () => isActuallyReady(record) && setTimeout(callback, 0);

  record.whenReady(listener);
  record.subscribe(listener);
}

client.login();

const record = client.record.getRecord("hello-world");

whenActuallyReady(record, () => {
  console.log(
    "event: whenActuallyReady",
    `ready: ${isActuallyReady(record)}`,
    JSON.stringify(record.get())
  );
});

I'm basically directly accessing the state machine to make sure that the record at least had some interactions with the server and tried merging things... Notice that I needed a setTimeout there because there's a race condition somewhere in the client's logic where the callbacks for record.whenReady and record.subscribe are being called BEFORE the remote data gets merged.

alanhoff avatar Sep 12 '19 15:09 alanhoff

Yeah your problem is that you are loading records while offline.

await client.login()

Should fix it. Offline disabled just means we don't set or store data in a local store. However record data is still stored in memory during connection loss and can be edited, which is why I can't disable some aspects and not others.

This functionality can only be disabled if you are in readOnly mode, since in that case you can't edit the data.

yasserf avatar Sep 13 '19 10:09 yasserf

I need to look into why your subscribes are being called like that though!

yasserf avatar Sep 13 '19 10:09 yasserf