Skip to content

Commit

Permalink
Add ignoreStaleLoad and expose TS types
Browse files Browse the repository at this point in the history
  • Loading branch information
MZanggl committed Jun 25, 2020
1 parent 015695f commit 4d6d881
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 9 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ promistate(async function callback() {
| ------------- |-- |:-------------:| -----:|
| catchErrors | boolean | true | You already use something like an ErrorBoundary component for catching errors |
| defaultValue | any | null | You already have a value at hand, or want to default it to an empty array, object, etc. |
| ignoreStaleLoad | boolean | false | If you "load" while there is already a promise pending, this will ignore any stale promise results. By calling "reset" you can also cancel promises this way. |
| ignoreLoadWhenPending | boolean | false | Prevent an event being fired twice e.g. when clicking a button. With this boolean set, while the first promise is still pending, subsequent loads would be ignored (not deferred!). When a subsequent load gets ignored, the "load" method returns the status "IGNORED" |
| isEmpty | Function | undefined | Say, the result is `{ page: 1, items: [] }`, the default "isEmpty" would always evaluate to false since a filled object is considered not empty. You can tweak the check like this: `{ isEmpty: value => value.items.length < 1 }` |

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "promistate",
"version": "1.1.1",
"version": "1.2.2",
"description": "Manage promise state easily",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
19 changes: 16 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CallbackArgs, Callback, Options, Status, State } from './types'
import { CallbackArgs, Callback, Options, Status, Result } from './types'

function isEmptyDefaultCheck<T>(value: T | null) {
if (Array.isArray(value)) {
Expand All @@ -11,15 +11,17 @@ function isEmptyDefaultCheck<T>(value: T | null) {
return value === undefined || value === null
}

function promistate<T>(callback: Callback<T>, options: Partial<Options<T>> = {}) : State<T> {
function promistate<T>(callback: Callback<T>, options: Options<T> = {}) : Result<T> {
const {
catchErrors = true,
defaultValue = null,
ignoreLoadWhenPending = false,
isEmpty = isEmptyDefaultCheck,
ignoreStaleLoad = false
} = options

return {
timesInitiated: 0,
timesSettled: 0,
value: defaultValue,
isPending: false,
Expand All @@ -34,25 +36,34 @@ function promistate<T>(callback: Callback<T>, options: Partial<Options<T>> = {})
this.isPending = false
this.error = null
this.timesSettled = 0
this.timesInitiated++
},

async load(...args: CallbackArgs) {
if (ignoreLoadWhenPending && this.isPending) {
return Status.IGNORED
}

const timesInitiated = this.timesInitiated + 1
this.timesInitiated = timesInitiated
this.isPending = true
this.error = null

return Promise.resolve(callback.apply(this, args))
.then((result: T) => {
if (ignoreStaleLoad && this.timesInitiated !== timesInitiated) {
return Status.IGNORED
}
this.timesSettled = this.timesSettled + 1
this.value = result
this.isPending = false
return Status.RESOLVED
})
.catch((error: Error) => {
this.timesSettled = this.timesSettled + 1
if (ignoreStaleLoad && this.timesInitiated !== timesInitiated) {
return Status.IGNORED
}
this.timesSettled++
this.isPending = false
this.value = defaultValue
this.error = error
Expand All @@ -67,4 +78,6 @@ export default promistate

export {
Status as PromistateStatus,
Result as PromistateResult,
Options as PromistateOptions,
}
12 changes: 7 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
export interface Options<T> {
catchErrors: boolean;
defaultValue: T;
ignoreLoadWhenPending: boolean;
isEmpty<T>(value: T | null): boolean;
catchErrors?: boolean;
defaultValue?: T;
ignoreLoadWhenPending?: boolean;
isEmpty?<T>(value: T | null): boolean;
ignoreStaleLoad?: boolean;
}

export enum Status {
Expand All @@ -14,7 +15,8 @@ export enum Status {
export type Callback<T> = (...args: CallbackArgs) => Promise<T>
export type CallbackArgs = any[]

export interface State<T> {
export interface Result<T> {
timesInitiated: number;
timesSettled: number;
value: T | null;
isPending: boolean;
Expand Down
13 changes: 13 additions & 0 deletions test/promistate.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,16 @@ test('updates counter after loading resource', async (assert) => {
state.reset()
assert.equal(state.timesSettled, 0)
})

test('automatically cancels return of promise value when another promise was initiated', async assert => {
const state = promistate((val, wait) => {
return new Promise((resolve) => setTimeout(() => resolve(val), wait))
}, { ignoreStaleLoad: true })

const promise = state.load(1, 50)
state.reset()
await state.load(2, 10)
await promise

assert.equal(state.value, 2)
})

0 comments on commit 4d6d881

Please sign in to comment.