-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Closed
Description
This proposal aims to making working with promises easier.
Some ideas:
- Expose
observable.promise(Promise)than returns{ state, value, promise }. In other words,fromPromiseof the mobx-utils package observable(Promise)and@observable x = Promisewill automatically convert to a promise observable (e.g. callobservable.promise)
3 Allow@computedto return Promises, and if so, convert this again in a promise structure.
Example code:
class User {
@observable name
@computed
get profile() {
// return a promise
return fetch(`/endpoint/${name}`)
}
@computed
get profileImageUrl() {
// pluck the image from the profile promise
return this.profile.state === "fullfilled"
? this.profile.value.image
: "/defaultavatar.png"
}
@computed({ initialValue: [] })
get tags() {
// chain another promise onto the profile promise
return this.profile.then(() =>
fetch(`/endpoint/${name}/tags`)
})
}
@computed
get isLoading() {
// are we fetching data?
return this.profile.state === "pending" || this.tags.state === "pending"
}
@computed
get isLoadingTakingLong() {
// provide spinner if loading takes long!
return Promise.race([
new Promise(resolve => setTimeout(() => resolve(true), 1000),
new Promise(resolve => this.isLoading.then((done) => { if (done) resolve(false) })
])
}
}
const UserView = observer(({ user }) => (
<div>
<h1>{user.name}<h1/>
{ user.isLoadingTakingLong.value && <Spinner /> }
<img src={user.profileImageUrl} />
{ user.tags.value.join(" - ") }
</div>
))I checked, this will even work correct with debug tools like trace.
Problems
The problem with the decorators is that their return type signature is incorrect; as the type Promise<T> would be converted into Promise<T> & { state: "pending" | "fullfilled" | "error", value: T | undefined | error }
Possible solutions:
@computed
get profile() {
// upcast
return fetch(`/endpoint/${name}`) as IObservablePromise
}
@computed
get profile() {
// wrapper fn (needing a better name)
return observablify(`/endpoint/${name}`)
}
// ... but still:
@computed
get profile() {
// upcast
const p fetch(`/endpoint/${name}`) as IObservablePromise
p.state // undefined!
return p
}Caveat: only the first part of a computed will be tracked. But that is explainable and makes it somewhere easy as well.
rzcoder, mixtur, LiranBri and fi3ework