Add document coordination methods to ViewRegistry

These will assist in updating and reading the DOM in a non-blocking
manner across components.
This commit is contained in:
Nathan Sobo
2015-02-19 14:26:39 -07:00
parent d337c88aea
commit de4d995190
2 changed files with 119 additions and 0 deletions

View File

@@ -85,3 +85,77 @@ describe "ViewRegistry", ->
expect(registry.getView(new TestModel) instanceof TestView).toBe true
disposable.dispose()
expect(-> registry.getView(new TestModel)).toThrow()
describe "::updateDocument(fn) and ::readDocument(fn)", ->
frameRequests = null
beforeEach ->
frameRequests = []
spyOn(window, 'requestAnimationFrame').andCallFake (fn) -> frameRequests.push(fn)
it "performs all pending writes before all pending reads on the next animation frame", ->
events = []
registry.updateDocument -> events.push('write 1')
registry.readDocument -> events.push('read 1')
registry.readDocument -> events.push('read 2')
registry.updateDocument -> events.push('write 2')
expect(events).toEqual []
expect(frameRequests.length).toBe 1
frameRequests[0]()
expect(events).toEqual ['write 1', 'write 2', 'read 1', 'read 2']
frameRequests = []
events = []
disposable = registry.updateDocument -> events.push('write 3')
registry.updateDocument -> events.push('write 4')
registry.readDocument -> events.push('read 3')
disposable.dispose()
expect(frameRequests.length).toBe 1
frameRequests[0]()
expect(events).toEqual ['write 4', 'read 3']
it "pauses DOM polling when reads or writes are pending", ->
spyOn(window, 'setInterval').andCallFake(fakeSetInterval)
spyOn(window, 'clearInterval').andCallFake(fakeClearInterval)
events = []
registry.pollDocument -> events.push('poll')
registry.updateDocument -> events.push('write')
registry.readDocument -> events.push('read')
advanceClock(registry.documentPollingInterval)
frameRequests[0]()
expect(events).toEqual ['write', 'read']
advanceClock(registry.documentPollingInterval)
expect(events).toEqual ['write', 'read', 'poll']
describe "::pollDocument(fn)", ->
it "calls all registered reader functions on an interval until they are disabled via a returned disposable", ->
spyOn(window, 'setInterval').andCallFake(fakeSetInterval)
events = []
disposable1 = registry.pollDocument -> events.push('poll 1')
disposable2 = registry.pollDocument -> events.push('poll 2')
expect(events).toEqual []
advanceClock(registry.documentPollingInterval)
expect(events).toEqual ['poll 1', 'poll 2']
advanceClock(registry.documentPollingInterval)
expect(events).toEqual ['poll 1', 'poll 2', 'poll 1', 'poll 2']
disposable1.dispose()
advanceClock(registry.documentPollingInterval)
expect(events).toEqual ['poll 1', 'poll 2', 'poll 1', 'poll 2', 'poll 2']
disposable2.dispose()
advanceClock(registry.documentPollingInterval)
expect(events).toEqual ['poll 1', 'poll 2', 'poll 1', 'poll 2', 'poll 2']

View File

@@ -42,9 +42,14 @@ Grim = require 'grim'
# ```
module.exports =
class ViewRegistry
documentPollingInterval: 200
constructor: ->
@views = new WeakMap
@providers = []
@documentWriters = []
@documentReaders = []
@documentPollers = []
# Essential: Add a provider that will be used to construct views in the
# workspace's view layer based on model objects in its model layer.
@@ -150,3 +155,43 @@ class ViewRegistry
findProvider: (object) ->
find @providers, ({modelConstructor}) -> object instanceof modelConstructor
updateDocument: (fn) ->
@documentWriters.push(fn)
@requestDocumentUpdate()
new Disposable =>
@documentWriters = @documentWriters.filter (writer) -> writer isnt fn
readDocument: (fn) ->
@documentReaders.push(fn)
@requestDocumentUpdate()
new Disposable =>
@documentReaders = @documentReaders.filter (reader) -> reader isnt fn
pollDocument: (fn) ->
@startPollingDocument() if @documentPollers.length is 0
@documentPollers.push(fn)
new Disposable =>
@documentPollers = @documentPollers.filter (poller) -> poller isnt fn
@stopPollingDocument() if @documentPollers.length is 0
requestDocumentUpdate: ->
unless @documentUpdateRequested
@documentUpdateRequested = true
@stopPollingDocument()
requestAnimationFrame(@performDocumentUpdate)
performDocumentUpdate: =>
@documentUpdateRequested = false
@startPollingDocument()
writer() while writer = @documentWriters.shift()
reader() while reader = @documentReaders.shift()
startPollingDocument: ->
@pollIntervalHandle = window.setInterval(@performDocumentPoll, @documentPollingInterval)
stopPollingDocument: ->
window.clearInterval(@pollIntervalHandle)
performDocumentPoll: =>
poller() for poller in @documentPollers