Last active
June 21, 2019 19:45
-
-
Save heyimalex/bdf0f3fc90d8fb755a2e29ca4d6931db to your computer and use it in GitHub Desktop.
Primitives for representing promise states
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PromiseState provides primitives for representing a promise's "state" as a | |
// value. | |
// | |
// That's kinda abstract so let's think through it. At any given point in time | |
// a promise is either pending, resolved, or rejected. There's no real | |
// synchronous way to access this promise "state", promises only give us `then` | |
// and callbacks. | |
// | |
// In react we usually care about rendering _all_ of these possible states. For | |
// example, we want to show a spinner when it's loading, the results when it | |
// succeeds, or an error message when it fails. The normal pattern is to track | |
// all of this in state: you setState({ pending: true }), make the request, and | |
// then setState({ pending: false, result: value }) in the promise callback. | |
// | |
// In my experience, every component that does this has its own little quirks; | |
// some call the state variable "pending", some call it "loading", "results" vs | |
// "value", "error" vs "reason". In almost every case you're adding some | |
// ambiguous sounding key to the top level state; god forbid you need to track | |
// the state of _two_ promises in a single component. Now your state has | |
// fooPending, barPending, fooResult, barResult, fooError, barError. You need | |
// to remember to clear errors and results every time the promise gets | |
// "replaced". Tracking whether a request has been made at all also needs to be | |
// taken care of. And the whole thing is a pain to use with typescript: we | |
// _know_ that when pending is false results is defined, but we still need to | |
// do undefined checks to satisfy the compiler. | |
// | |
// This file is a solution to that problem. It doesn't _handle_ anything | |
// relating to promises per-se, but it gives very convenient primitives for | |
// _representing_ their state as values in a way that pairs very nicely with | |
// typescript. | |
interface PromiseStatePending<M> { | |
status: "pending"; | |
pending: true; | |
fulfilled: false; | |
rejected: false; | |
value: null; | |
reason: null; | |
meta: M; | |
} | |
interface PromiseStateFulfilled<V, M> { | |
status: "fulfilled"; | |
pending: false; | |
fulfilled: true; | |
rejected: false; | |
value: V; | |
reason: null; | |
meta: M; | |
} | |
interface PromiseStateRejected<R, M> { | |
status: "rejected"; | |
pending: false; | |
fulfilled: false; | |
rejected: true; | |
value: null; | |
reason: R; | |
meta: M; | |
} | |
type PromiseState<V = any, M = undefined, R = any> = | |
| PromiseStatePending<M> | |
| PromiseStateFulfilled<V, M> | |
| PromiseStateRejected<R, M>; | |
function create(meta?: undefined): PromiseStatePending<undefined>; | |
function create<M>(meta: M): PromiseStatePending<M>; | |
function create<M>(meta: M): PromiseStatePending<M> { | |
return { | |
status: "pending", | |
pending: true, | |
fulfilled: false, | |
rejected: false, | |
value: null, | |
reason: null, | |
meta: meta | |
}; | |
} | |
function resolve<V>( | |
value: V, | |
meta?: undefined | |
): PromiseStateFulfilled<V, undefined>; | |
function resolve<V, M>(value: V, meta: M): PromiseStateFulfilled<V, M>; | |
function resolve<V, M>(value: V, meta: M): PromiseStateFulfilled<V, M> { | |
return { | |
status: "fulfilled", | |
pending: false, | |
fulfilled: true, | |
rejected: false, | |
value: value, | |
reason: null, | |
meta: meta | |
}; | |
} | |
function reject<R>( | |
reason: R, | |
meta?: undefined | |
): PromiseStateRejected<R, undefined>; | |
function reject<R, M>(reason: R, meta: M): PromiseStateRejected<R, M>; | |
function reject<R, M>(reason: R, meta: M): PromiseStateRejected<R, M> { | |
return { | |
status: "rejected", | |
pending: false, | |
fulfilled: false, | |
rejected: true, | |
value: null, | |
reason: reason, | |
meta: meta | |
}; | |
} | |
const PromiseState = { | |
create, | |
resolve, | |
reject | |
}; | |
export default PromiseState; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment