Skip to content

Commit d1f63be

Browse files
committed
WIP
1 parent 4e6f084 commit d1f63be

1 file changed

Lines changed: 56 additions & 4 deletions

File tree

src/core/computedvalue.ts

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import {
2727
invariant,
2828
Lambda,
2929
primitiveSymbol,
30-
toPrimitive
30+
toPrimitive,
31+
isThennable
3132
} from "../utils/utils"
3233
import { isSpyEnabled, spyReport } from "./spy"
3334
import { autorun } from "../api/autorun"
@@ -47,8 +48,11 @@ export interface IComputedValueOptions<T> {
4748
equals?: IEqualsComparer<T>
4849
context?: any
4950
requiresReaction?: boolean
51+
defaultValue?: T
5052
}
5153

54+
let notInitialized
55+
5256
/**
5357
* A node in the state dependency root that observes other nodes, and can be observed itself.
5458
*
@@ -82,7 +86,8 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
8286
lowestObserverState = IDerivationState.UP_TO_DATE
8387
unboundDepsCount = 0
8488
__mapid = "#" + getNextId()
85-
protected value: T | undefined | CaughtException = new CaughtException(null)
89+
protected value: T | undefined | CaughtException = notInitialized ||
90+
(notInitialized = new CaughtException("COMPUTED_NOT_INITIALIZED"))
8691
name: string
8792
triggeredBy: string
8893
isComputing: boolean = false // to check for cycles
@@ -93,6 +98,8 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
9398
public scope: Object | undefined
9499
private equals: IEqualsComparer<any>
95100
private requiresReaction
101+
defaultValue: T // used by suspended computed values
102+
pendingPromise?: PromiseLike<any>
96103

97104
/**
98105
* Create a new computed value based on a function expression.
@@ -119,6 +126,7 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
119126
: comparer.default)
120127
this.scope = options.context
121128
this.requiresReaction = !!options.requiresReaction
129+
if ("defaultValue" in options) this.defaultValue = options.defaultValue!
122130
}
123131

124132
onBecomeStale() {
@@ -148,7 +156,9 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
148156
}
149157
const result = this.value!
150158

151-
if (isCaughtException(result)) throw result.cause
159+
if (isCaughtException(result)) {
160+
throw result.cause
161+
}
152162
return result
153163
}
154164

@@ -192,6 +202,7 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
192202
const wasSuspended =
193203
/* see #1208 */ this.dependenciesState === IDerivationState.NOT_TRACKING
194204
const newValue = (this.value = this.computeValue(true))
205+
if (this.pendingPromise) return false
195206
return (
196207
wasSuspended ||
197208
isCaughtException(oldValue) ||
@@ -206,14 +217,26 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
206217
let res: T | CaughtException
207218
if (track) {
208219
res = trackDerivedFunction(this, this.derivation, this.scope)
220+
if (isCaughtException(res) && isThennable(res.cause)) {
221+
this.awaitPromise(res.cause)
222+
res = this.value === notInitialized ? this.defaultValue : this.value!
223+
}
209224
} else {
210225
if (globalState.disableErrorBoundaries === true) {
211226
res = this.derivation.call(this.scope)
212227
} else {
213228
try {
214229
res = this.derivation.call(this.scope)
215230
} catch (e) {
216-
res = new CaughtException(e)
231+
if (isThennable(e)) {
232+
res = new CaughtException(
233+
new Error(
234+
"[mobx] Untracked computed values cannot suspend; nobody will await their value"
235+
)
236+
)
237+
} else {
238+
res = new CaughtException(e)
239+
}
217240
}
218241
}
219242
}
@@ -227,6 +250,35 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
227250
this.value = undefined // don't hold on to computed value!
228251
}
229252

253+
awaitPromise(promise: PromiseLike<T>) {
254+
if (promise === this.pendingPromise) return
255+
this.pendingPromise = promise
256+
promise.then(
257+
newValue => {
258+
debugger
259+
if (promise !== this.pendingPromise) return
260+
this.pendingPromise = undefined
261+
const oldValue = this.value
262+
if (isCaughtException(oldValue) || !this.equals(oldValue, newValue)) {
263+
startBatch()
264+
propagateMaybeChanged(this)
265+
this.value = newValue
266+
propagateChangeConfirmed(this)
267+
endBatch()
268+
}
269+
},
270+
error => {
271+
if (promise !== this.pendingPromise) return
272+
this.pendingPromise = undefined
273+
startBatch()
274+
propagateMaybeChanged(this)
275+
this.value = new CaughtException(error)
276+
propagateChangeConfirmed(this)
277+
endBatch()
278+
}
279+
)
280+
}
281+
230282
observe(listener: (change: IValueDidChange<T>) => void, fireImmediately?: boolean): Lambda {
231283
let firstTime = true
232284
let prevValue: T | undefined = undefined

0 commit comments

Comments
 (0)