New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add batching #295
Add batching #295
Conversation
Codecov Report
@@ Coverage Diff @@
## master #295 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 22 23 +1
Lines 802 820 +18
=====================================
+ Hits 802 820 +18
Continue to review full report at Codecov.
|
examples/canvas/app/updater.js
Outdated
}, options) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be cool for this to be a microcosm-batch-updates
module, or something. I imagine there's really only one way to do this.
2a24bcd
to
1435301
Compare
Microcosm is [Flux](https://facebook.github.io/flux/) with first-class | ||
actions and state sandboxing. | ||
The Microcosm class provides a centralized place to store application | ||
state, dispatch actions, and track changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if we should keep the reference to Flux for the "This is like X" aspect of documentation, otherwise this is an awesome change. 👍
docs/api/microcosm.md
Outdated
you provide to Microcosm are passed into the `setup` lifecycle method: | ||
|
||
```javascript | ||
import Autosave from './effects/autosave' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this example code demonstrate the concept without introducing effects? to keep the section as simple as possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Good idea.
@@ -0,0 +1,132 @@ | |||
# Batch Updates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this doc is really good, 👏
/** | ||
* The central tree data structure that is used to calculate state for | ||
* a Microcosm. Each node in the tree is an action. Branches are | ||
* changes over time. | ||
* @constructor | ||
*/ | ||
export default function History (limit) { | ||
export default function History (config) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with this change, would it make sense to warn when config is not an object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so. History is never instantiated directly, it always passes through new Microcosm()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh I see, sounds good. somehow I thought I was looking at the Microcosm constructor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@cwmanning good call on the test. I discovered a bug where you could get stuck in Which made me realize... we don't need to request any more "updates" if the last update hasn't fired yet. That can reduce the complexity of the updater significantly. Instead of doing: // A lot of browsers don't support requestIdleCallback, so we patch it
import 'ric'
// Never let the user wait more than 24 milliseconds for an update
const options = { timeout: 24 }
export default function requestIdleBatch () {
// Batching strategies return a function. This allows you to
// maintain state within the closure above. Here, we keep track of
// the last frame of work
let frame = null
return update => {
if (frame == null) {
frame = requestIdleCallback(() => {
frame = null
update()
}, options)
}
}
} We could just do: // A lot of browsers don't support requestIdleCallback, so we patch it
import 'ric'
// Never let the user wait more than 24 milliseconds for an update
const options = { timeout: 24 }
export default function requestIdleBatch () {
return update => requestIdleCallback(update, options)
} I still want to return a function, because it would allow you to do things like setup logging or some other system. But you don't have to keep track of prior frames, which is really nice. What do you think? |
@cwmanning technically now, you could also just do: export default function requestIdleBatch () {
return update => setTimeout(update, 24)
} |
@nhunzaker I like the simplified updater example. Might include the really simple one ( |
@cwmanning Done, what do you think? |
@leobauza Do these documentation updates feel good? Anything we should change? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Neat stuff, curious what peoples' thoughts are around simplifying the interface for this, and just exposing a configurable batch delay option that would instantiate an in-house updater. Reading through the Batch Updates docs is neat, but by the end of the docs I got the sense that there's really an optimal way to do things (use |
docs/recipes/batch-updates.md
Outdated
test("it increases the number when the stepper is clicked", function () { | ||
let app = mount(<App />) | ||
|
||
app.find('#stepper).simulate('click') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be app.find('#stepper')
(missing closing quote mark after #stepper
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
👍 Those docs are 💯 . Also, I think @efatsi is on to something with providing a simple mechanism to use the default recommended batch update operation. |
@mackermedia @efatsi Hmm, what do you think about making this option |
|
i was thinking |
agreeing with @mackermedia, passing in functions allows for greater control but requires more underlying knowledge of microcosm, mainly - what's passed in to that function. My thinking would be something like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Microcosm is [Flux](https://facebook.github.io/flux/) with first-class | ||
actions and state sandboxing. | ||
The Microcosm class provides a centralized place to store application | ||
state, dispatch actions, and track changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am wondering if we should keep the reference to Flux for the "This is like X" aspect of documentation, otherwise this is an awesome change. 👍
@efatsi, @mackermedia Here's my thinking. I want a safe default, with an escape hatch. I think we can do this without exposing a lot of complexity. I think we need to add 2-3 options:
The updater function will get the options passed into Microcosm. Our default updater function would look like: function requestIdleBatch (options) {
if (options.batch) {
return update => requestIdleCallback(update, { timeout: options.batchInterval })
} else {
return update => update()
}
} This allows you:
I'm still debating whether or not we need What do you think? |
Honestly, I don't really know of the use-case of when I'd reach for it. It sounds like when the app starts to get janky from a lot of actions resolving and re-rendering when I want an animation to finish. |
@mackermedia Yep. That's precisely the use case. When you have a lot of actions firing, causing meaningful changes, and the successive change events cause expensive repaints. Just having |
👍 to |
|
This commit exposes control over the release process so that changes can be batched together. It also ensures that this does not break `history.wait()`. Additionally, it updates the canvas example to use a requestIdleCallback batching strategy.
This commit exposes control over the release process so that changes can be batched together. It also ensures that this does not break
history.wait()
.Additionally, it updates the canvas example to use a requestIdleCallback batching strategy. This improves write throughput about 60-80%, but I'm mostly looking forward to preventing duplicate renders for loading states that finish really quickly.
http://probable-pocket.surge.sh/