Skip to content

Instantly share code, notes, and snippets.

@WreckedAvent
Last active July 23, 2016 23:52
Show Gist options
  • Save WreckedAvent/7b7fe28895c9d8a376766422d045b270 to your computer and use it in GitHub Desktop.
Save WreckedAvent/7b7fe28895c9d8a376766422d045b270 to your computer and use it in GitHub Desktop.
import * as m from 'mithril'
import * as ko from 'knockout'
// both mithril and knockout have a concept of 'observables'
// mithril calls this a 'prop' for some reason or another
var mObs = m.prop()
var kObs = ko.observable()
// observables have a fairly simple operation - call it with nothing
// to get the current state, call it with something to set the state
mObs(25)
kObs(25)
console.log(mObs(), kObs()) // 25 25
// until the rewrite, however, the mithril observables had no dependency tracking
// and no way to create child observables. You just had to call the observables each time
var first = m.prop('Jane')
var last = m.prop('Doe')
var full = () => `${first()} ${last()}`
console.log(full()) // Jane Doe, both first and last called
first('Bob')
console.log(full()) // Bob Doe, both first and last called
console.log(full()) // Bob Doe, both first and last called
console.log(full()) // Bob Doe, both first and last called
// knockout, however, has a concept of dependency tracking, and subscriptions. You can write
// the above code with a direct child relationship. These were called 'computed' observables
first = ko.observable('Jane')
last = ko.observable('Doe')
// as this is created, ko will create a subscription on these two values, and only update them as they are changed
full = ko.computed(() => `${first()} ${last()}`)
// both first and last will be called immediately as this is set up
console.log(full()) // Jane Doe, neither first or last were called
first('Bob') // changing this EAGERLY changes name to have the new value, first and last are called here
console.log(full()) // Bob Doe, neither first or last were called
console.log(full()) // Bob Doe, neither first or last were called
console.log(full()) // Bob Doe, neither first or last were called
// this is because knockout manages dependency graphs, and update sthe children as their dependencies change
// mithril props are just very simple getter/setter factories and not 'true' observables
// however, in the rewrite, this is different
first = m.prop('Jane')
last = m.prop('Doe')
full = m.prop.combine((f, l) => `${f()} ${l()}`, [first, last])
// this behaves similarly to the knockout code, however it is quite a bit more cumbersome to set up
// knockout determines the dependency graphs automatically, mithril requires us to manually merge and specify them
// I intentionally named them 'f' and 'l' to show that they were not the same as the outer first/last props
console.log(full()) // Jane Doe, neither first nor last were evaluated
first('Bob') // full is updated eagerly here
console.log(full()) // Bob Doe, neither first nor last were evaluated
console.log(full()) // Bob Doe, neither first nor last were evaluated
console.log(full()) // Bob Doe, neither first nor last were evaluated
// this is also interesting in the context of mithril, since we can return *more* than just pure values
// ... we can return vdom fragments
var permissions = m.prop('no')
var permView = permissions.run(perms => m('p', `You have ${perms} permissions`))
// entire apps could be expressed as this cascading dependency graph
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment