Commit 2244524e466413910788cad98baa61df4f340866
computed: resolve and broadcast inner value if observable
Matt McKegg committed on 7/15/2016, 3:04:19 AMParent: ec39f72902c3a64aa9841e339bd53cf60182953c
Files changed
computed.js | changed |
computed.js | ||
---|---|---|
@@ -1,18 +1,30 @@ | ||
1 | 1 … | /* A lazy binding take on computed */ |
2 | -// doesn't start watching observables until itself is watched, and then releases if unwatched | |
3 | -// avoids memory/watcher leakage | |
2 … | +// - doesn't start watching observables until itself is watched, and then releases if unwatched | |
3 … | +// - avoids memory/watcher leakage | |
4 … | +// - attaches to inner observables if these are returned from value | |
5 … | +// - doesn't broadcast if value is same as last value (and is `value type` or observable, can't make assuptions about reference types) | |
4 | 6 … | |
5 | 7 … | var resolve = require('./resolve') |
8 … | +var isObservable = require('./is-observable') | |
6 | 9 … | |
7 | 10 … | module.exports = computed |
8 | 11 … | |
9 | 12 … | function computed (observables, lambda) { |
13 … | + if (!Array.isArray(observables)) { | |
14 … | + observables = [observables] | |
15 … | + } | |
16 … | + | |
10 | 17 … | var values = [] |
11 | 18 … | var releases = [] |
12 | 19 … | var computedValue = null |
20 … | + | |
21 … | + var inner = null | |
22 … | + var releaseInner = null | |
23 … | + | |
13 | 24 … | var live = false |
14 | 25 … | var lazy = false |
26 … | + var initialized = false | |
15 | 27 … | var listeners = [] |
16 | 28 … | |
17 | 29 … | var result = function (listener) { |
18 | 30 … | if (!listener) { |
@@ -49,16 +61,25 @@ | ||
49 | 61 … | if (typeof observables[i] === 'function') { |
50 | 62 … | releases.push(observables[i](onUpdate)) |
51 | 63 … | } |
52 | 64 … | } |
65 … | + if (inner) { | |
66 … | + releaseInner = inner(onInnerUpdate) | |
67 … | + } | |
53 | 68 … | live = true |
54 | 69 … | lazy = true |
55 | 70 … | } |
56 | 71 … | } |
57 | 72 … | |
58 | 73 … | function unlisten () { |
59 | 74 … | if (live) { |
60 | 75 … | live = false |
76 … | + | |
77 … | + if (releaseInner) { | |
78 … | + releaseInner() | |
79 … | + releaseInner = null | |
80 … | + } | |
81 … | + | |
61 | 82 … | while (releases.length) { |
62 | 83 … | releases.pop()() |
63 | 84 … | } |
64 | 85 … | } |
@@ -73,18 +94,42 @@ | ||
73 | 94 … | values[i] = newValue |
74 | 95 … | } |
75 | 96 … | } |
76 | 97 … | |
77 | - if (changed) { | |
98 … | + if (changed || !initialized) { | |
99 … | + initialized = true | |
78 | 100 … | var newComputedValue = lambda.apply(null, values) |
79 | - if (newComputedValue !== computedValue || typeof newComputedValue === 'object') { | |
80 | - computedValue = newComputedValue | |
101 … | + if (newComputedValue !== computedValue || (typeof newComputedValue === 'object' && !isObservable(newComputedValue))) { | |
102 … | + if (releaseInner) { | |
103 … | + releaseInner() | |
104 … | + inner = releaseInner = null | |
105 … | + } | |
106 … | + | |
107 … | + if (isObservable(newComputedValue)) { | |
108 … | + // handle returning observable from computed | |
109 … | + computedValue = newComputedValue() | |
110 … | + inner = newComputedValue | |
111 … | + if (live) { | |
112 … | + releaseInner = inner(onInnerUpdate) | |
113 … | + } | |
114 … | + } else { | |
115 … | + computedValue = newComputedValue | |
116 … | + } | |
81 | 117 … | return true |
82 | 118 … | } |
83 | 119 … | } |
84 | 120 … | return false |
85 | 121 … | } |
86 | 122 … | |
123 … | + function onInnerUpdate (value) { | |
124 … | + if (value !== computedValue || typeof newComputedValue === 'object') { | |
125 … | + computedValue = value | |
126 … | + for (var i = 0, len = listeners.length; i < len; i++) { | |
127 … | + listeners[i](computedValue) | |
128 … | + } | |
129 … | + } | |
130 … | + } | |
131 … | + | |
87 | 132 … | function onUpdate () { |
88 | 133 … | if (update()) { |
89 | 134 … | for (var i = 0, len = listeners.length; i < len; i++) { |
90 | 135 … | listeners[i](computedValue) |
Built with git-ssb-web