Skip to content

Commit

Permalink
Handle cancellable promise error.
Browse files Browse the repository at this point in the history
  • Loading branch information
rokbar-nosto committed Oct 24, 2023
1 parent 4d908e0 commit 3e70911
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 13 deletions.
17 changes: 9 additions & 8 deletions src/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { bindClickOutside, findAll } from './utils/dom'
import { bindInput } from './utils/input'
import { History } from './history'
import { Limiter, LimiterError } from './utils/limiter'
import { CancellableError } from './utils/promise'

/**
* @group Autocomplete
Expand Down Expand Up @@ -44,22 +45,19 @@ export function autocomplete<State = DefaultState>(
return
}

let lastRenderTime = Date.now()

const input = bindInput(inputElement, {
onInput: async (value) => {
const requestTime = Date.now()

try {
await limiter.limited(() => {
return actions.updateState(value).then((state) => {
if (requestTime >= lastRenderTime) {
return actions.updateState(value).then(
(state) => {
dropdown.update(state)
}
})
)
})
} catch (err) {
if (!(err instanceof LimiterError)) {
if (!(err instanceof LimiterError || err instanceof CancellableError)) {
throw err
}
}
Expand Down Expand Up @@ -165,7 +163,10 @@ function createInputDropdown<State = DefaultState>({

return new Dropdown<State>(
dropdownElement,
actions.updateState(input.value),
actions.updateState(input.value).then(
(state) => state,
() => ({} as State),
),
config.render,
config.submit,
(value) => (input.value = value),
Expand Down
15 changes: 12 additions & 3 deletions src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,23 @@ export const getStateActions = <State>({

if (inputValue && inputValue.length >= minQueryLength) {
cancellable = makeCancellable(fetchState(inputValue, config))
return cancellable.promise
return cancellable.promise.then(
(s) => s as State,
(e) => {
throw e
},
)
} else if (history) {
return getHistoryState(inputValue ?? '')
}

return (
cancellable?.promise ??
AnyPromise.resolve({}).then((s) => s as State)
cancellable?.promise.then(
(s) => s as State,
(e) => {
throw e
},
) ?? AnyPromise.resolve({}).then((s) => s as State)
)
},
addHistoryItem: (item: string) => {
Expand Down
6 changes: 4 additions & 2 deletions src/utils/promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,18 @@ export let AnyPromise = 'Promise' in window ? window.Promise : SimplePromise

export type Cancellable<T> = { promise: PromiseLike<T>; cancel: () => void }

export class CancellableError extends Error {}

export function makeCancellable<T>(promise: PromiseLike<T>): Cancellable<T> {
let hasCanceled_ = false

const wrappedPromise = new AnyPromise<T>((resolve, reject) => {
promise.then(
(val) => {
hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)
hasCanceled_ ? reject(new CancellableError('cancelled promise')) : resolve(val)
},
(error) => {
hasCanceled_ ? reject({ isCanceled: true }) : reject(error)
hasCanceled_ ? reject(new CancellableError('cancelled promise')) : reject(error)
},
)
})
Expand Down

0 comments on commit 3e70911

Please sign in to comment.