@@ -27,7 +27,8 @@ import {
2727 invariant ,
2828 Lambda ,
2929 primitiveSymbol ,
30- toPrimitive
30+ toPrimitive ,
31+ isThennable
3132} from "../utils/utils"
3233import { isSpyEnabled , spyReport } from "./spy"
3334import { 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