import { computed , action , observable , runInAction , autorun } from "mobx" ; import { getGlobalState } from "./mobxShim" ; import { fromPromise , IPromiseBasedObservable , isPromiseBasedObservable } from "mobx-utils" ; import { Getter } from "./Getter" ; export function isPromiseLike < T > ( result ) { return result && typeof ( result as any ) . then === "function" ; } /**
 * PromisedComputedValue
 */ export interface PromisedComputedValue < T > extends Getter < T > { /** True if the promise is currently resolving */ readonly busy : boolean ; refresh ( ) : void ; getNonReactive ( ) : T ; } type PromiseResult < T > = { ok : true ; value : T ; } | { ok : false ; error : any ; } ; function value < T > ( value ) { return { ok : true , value } ; } function error < T > ( error ) { return { ok : false , error } ; } class PromisedComputed < T > implements PromisedComputedValue < T > { private cached : PromiseResult < T > ; @ observable private refreshCallCount : number ; @ computed private get currentState ( ) : IPromiseBasedObservable < PromiseResult < T > > | PromiseResult < T > { try { this . refreshCallCount ; const promiseOrValue = this . fetch ( ) ; return isPromiseLike ( promiseOrValue ) ? fromPromise ( promiseOrValue . then ( value , e => error < T > ( e ) ) ) : value ( promiseOrValue ) ; } catch ( x ) { return error < T > ( x ) ; } } constructor ( init , private readonly fetch , private disableReactionChecking ? ) { runInAction ( ( ) => this . refreshCallCount = 0 ) ; this . cached = value ( init ) ; } @ computed get busy ( ) { const s = this . currentState ; return ! ! ( isPromiseBasedObservable ( s ) && s . state === "pending" ) ; } @ action refresh ( ) { this . refreshCallCount ++ ; } get ( ) { if ( ! this . disableReactionChecking && ! getGlobalState ( ) . trackingDerivation ) { throw new Error ( "promisedComputed must be used inside reactions" ) ; } return this . value ; } /**
     * This exists purely to support scenarios such as unit tests that
     * want to verify the most recent value outside of a reactive context
     */ getNonReactive ( ) { let result = undefined ! ; autorun ( ( ) => result = this . get ( ) ) ( ) ; return result ; } @ computed private get value ( ) : T { const s = this . currentState ; const r = ! isPromiseBasedObservable ( s ) ? s : s . state === "fulfilled" ? s . value : this . cached ; this . cached = r ; if ( r . ok ) { return r . value ; } throw r . error ; } } param init param compute export function promisedComputed < T > ( init , compute ) { return new PromisedComputed < T > ( init , compute ) ; } export function promisedComputedInternal < T > ( init , compute ) { return new PromisedComputed < T > ( init , compute , true ) ; }