yorkie-team / yorkie-js-sdk

Yorkie JavaScript SDK
https://yorkie.dev/docs/js-sdk
Apache License 2.0
142 stars 93 forks source link

Change `subscribe()` to work synchronously from asynchronously #502

Closed chacha912 closed 1 year ago

chacha912 commented 1 year ago

Description: Yorkie provides a subscribe method that subscribes to document event and client event, follows the RxJS observer interface, and works asynchronously through promise. https://github.com/yorkie-team/yorkie-js-sdk/blob/fa89fbe993ff42fb4ce56708a53157c7761c2ec6/src/util/observable.ts#L37-L209

We want to change it to work synchronously. This will allow us to perform the same process as we did in text.onChanges inside the subscribe method.

Why:

chacha912 commented 1 year ago

I have added code to the text-onchanges branch, where I replaced text.onChanges with doc.subscribe in the codemirror example to test this issue.

Currently, there is a synchronization problem when local edits occur before the remote-changes are reflected in the CodeMirror view. To test this, you can modify the doc.subscribe() as shown below:

doc.subscribe((event) => {
    displayLog(doc, codemirror);
    setTimeout(() => {
        if (event.type === 'snapshot') {
            syncText();
        }
        if (event.type === 'remote-change') {
            for (const change of event.value) {
                const { actor, message, operations } = change;
                changeEventHandler(operations, actor);
            }
        }
    }, 3000);
});

By doing this, you can observe the problem, as shown in the image below:

sync

Let's consider this situation in a diagram. When applying remote edit operations, the Yorkie document is updated. However, since subscribe is invoked asynchronously, the changes may not yet be reflected in the CodeMirror view. Consequently, if local edits occur before remote changes are fully applied in the view, it can lead to a mismatch in synchronization.

image

chacha912 commented 1 year ago

Yorkie utilizes the RxJS observer pattern, which executes callbacks asynchronously. To achieve synchronous event execution, callbacks can be queued and executed sequentially when events are dispatched. For code examples, we can refer to the following samples from CodePair and Liveblocks:

easylogic commented 1 year ago

This got me thinking. Here are my thoughts

  1. there are 100 unspecified returning users.
  2. let's say 99 of them are still keystroking
  3. where is the point where I can keystroke?