DocHandles
Once you have a Repo with a NetworkAdapter and a StorageAdapter you can get down to the business of creating and working with DocHandles.
It's useful to understand a little about why we need a DocHandle. @automerge/automerge documents are fairly inert data structures. You can create a document, you can mutate it, you can generate sync messages to send elsewhere and you can receive sync messages from elsewhere. None of this is very "live" though. Because the document has no concept of a network, or of storage, you can't say "every time I change a document, tell everyone else about it and save the change to storage". This "live document" is what a DocHandle is. A DocHandle is a wrapper around a document managed by a Repo. It provides the following kinds of "liveness":
- Whenever you change the document using
DocHandle.changeorDocHandle.changeAtthe changes will be saved to the attachedStorageAdapterand sent to any connectedNetworkAdapters - Whenever a change is received from a connected peer the
DocHandlewill fire a "change" event - There is a concept of an ephemeral message, which you can send using
DocHandle.broadcast. Whenever aDocHandlereceives an ephemeral message it will fire a"ephemeral-message"event - You can wait for a
DocHandleto be loaded, or to be retrieved from another peer DocHandles have aURL, which can be used to uniquely refer to the document it wraps when requesting it from another peer
DocHandles are very useful, how do you obtain one?
Creating a DocHandle
This is the easy one, just call Repo.create. This creates a new document, stores it, and then enqueues messages to all connected peers informing them of the new document.
Waiting for a DocHandle
Typically you are not creating a new document, but working with an existing one. Maybe the document URL was stored in localStorage, maybe the URL was in the hash fragment of the browser, etc. In this case you use Repo.find to lookup the document. This means the DocHandle can be in several different states, to understand this we'll first look at the states in detail, then some convenience methods DocHandle exposes for waiting for different states.
DocHandle states
Repo.find will do two things simultaneously:
- Look in the attached
StorageAdapterto see if we have any data for the document - Send a request to any connected peers to ask if they have the document
These actions are asynchronous, as they complete the state of the document changes. This state is represented most explicitly in the HandleState enum, which has the following states:
IDLE- This is really just a start state, every dochandle immediately transitions to another stateAWAITING_NETWORK- in this state we are waiting for theNetworkAdapters to be ready to process messages. This typically occurs at application startup. MostNetworkAdapters have an asynchronous startup period. TheRepowaits until everyNetworkAdapterhas emitted areadyevent before beginning to request documentsLOADING- we are waiting for storage to finish trying to load this documentREQUESTING- we are waiting to hear back from other peers about this documentREADY- The document is available, either we created it, found it in storage, or someone sent it to usDELETED- The document was removed from the repoUNAVAILABLE- We don't have the document in storage and none of our peers have the document either
The transitions between these states look like this:
Note that every state can transition to DELETED, either via DocHandle.delete or Repo.delete.
One other point to note is that a DocHandle can be unavailable because we didn't have it in storage and no peers responded to our request for it, but then another peer comes online and sends us sync messages for the document and so it transitions to READY.
You can check what state a handle is in using DocHandle.inState.
Waiting for a handle to be ready
If all we care about is whether the document is ready then we can use a few different methods.
DocHandle.isReady()is a synchronous method which will returntrueif the document is readyDocHandle.whenReady()is an asynchronous method that will return when the handle is readyDocHandle.doc()is an asynchronous method which will return the value of the document when it is readyDocHandle.docSync()is a synchronous method which returns the value of the document if it is ready. This method will throw if the handle is not ready. Therefore you should guard calls todocSyncwith calls toisReady
Once the document is ready the value of the document (either DocHandle.doc() or DocHandle.docSync()) will be undefined if the document was unavailable, but otherwise will be an automerge document.