Skip to content

Commit

Permalink
fix build fail and add changes from main
Browse files Browse the repository at this point in the history
  • Loading branch information
aiste-tamo committed Oct 26, 2023
1 parent 348f879 commit 8a8f8d3
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 187 deletions.
80 changes: 0 additions & 80 deletions spec/custom.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import userEvent from '@testing-library/user-event'
import { autocomplete } from '../src'

import '@testing-library/jest-dom'
import { AnyPromise, SimplePromise } from '../src/utils/promise'

beforeAll(() => {
document.body.innerHTML = `
Expand Down Expand Up @@ -66,82 +65,3 @@ describe('autocomplete', () => {
expect(screen.getByText('keyword blue')).toBeVisible()
})
})

describe('SimplePromise', () => {
it('has resolve and reject callbacks', async () => {
const promise = new SimplePromise((resolve, reject) => {
// expect resolve and reject to be defined and to be functions which acceps one argument
expect(resolve).toBeDefined()
expect(reject).toBeDefined()
expect(typeof resolve).toBe('function')
expect(typeof reject).toBe('function')

// resolve promise with value of 1
setTimeout(() => {
resolve(1)
}, 0)
})

await promise
})

it('resolve() corectly returns value in .then block', async () => {
const promise = new SimplePromise((resolve) => {
setTimeout(() => {
resolve('promise')
}, 0)
})

let value;
promise.then((v) => {
value = v
})
await promise

expect(value).toBe('promise')
})

it('reject() correctly rejects with a value in .then block', async () => {
const promise = new SimplePromise((_, reject) => {
setTimeout(() => {
reject('error')
}, 0)
})

let error: any;


promise.then(() => {
return
}, (err) => {
error = err
return
})

await waitFor(() => {
expect(error).toBe('error')
}, {
timeout: 1000
})
})

it('resolves promise in .then block', async () => {
const promise = new SimplePromise((resolve) => {
setTimeout(() => {
resolve('promise')
}, 0)
})

let value;
promise.then(() => {
return new SimplePromise((resolve) => {
resolve('promise2')
})
}).then((v) => {
value = v
})

await promise;
expect(value).toBe('promise2')
})
})
1 change: 0 additions & 1 deletion spec/liquid.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {

import '@testing-library/jest-dom'
import { AnyPromise } from '../src/utils/promise'
import { DefaultState } from '../src/state'

const handleAutocomplete = () => {
autocomplete({
Expand Down
17 changes: 8 additions & 9 deletions src/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ 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 @@ -45,19 +44,22 @@ 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) => {
return actions.updateState(value).then((state) => {
if (requestTime >= lastRenderTime) {
dropdown.update(state)
}
)
})
})
} catch (err) {
if (!(err instanceof LimiterError || err instanceof CancellableError)) {
if (!(err instanceof LimiterError)) {
throw err
}
}
Expand Down Expand Up @@ -163,10 +165,7 @@ function createInputDropdown<State = DefaultState>({

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

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

return (
cancellable?.promise.then(
(s) => s as State,
(e) => {
throw e
},
) ?? AnyPromise.resolve({}).then((s) => s as State)
cancellable?.promise ??
AnyPromise.resolve({}).then((s) => s as State)
)
},
addHistoryItem: (item: string) => {
Expand Down
136 changes: 51 additions & 85 deletions src/utils/promise.ts
Original file line number Diff line number Diff line change
@@ -1,102 +1,70 @@
export class SimplePromise<T> implements PromiseLike<T> {
private status: string
private value: T
private onFulfilledCallbacks: Array<(value: T) => void>
private onRejectedCallbacks: Array<(value: any) => void>
class SimplePromise<T> implements PromiseLike<T> {
private value: T | undefined
private error: any
private fulfilled = false
private doneCallback: (() => void) | undefined = undefined

constructor(
handler: (resolve: (value: T) => void, reject: (reason?: any) => void) => void
callback: (
resolve: (value: T) => void,
reject: (value: any) => void,
) => void,
) {
this.value = null as T
this.status = 'pending'
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
callback(
(value) => {
if (!this.fulfilled) {
this.value = value
this.fulfilled = true

const resolve = (value: T) => {
if (this.status === 'pending') {
this.status = 'fulfilled'
this.value = value
this.onFulfilledCallbacks.forEach((fn) => fn(value))
}
}

const reject = (value: T) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.value = value
this.onRejectedCallbacks.forEach((fn) => fn(value))
}
}
if (this.doneCallback) {
this.doneCallback()
}
}
},
(error) => {
if (!this.fulfilled) {
this.error = error
this.fulfilled = true

try {
handler(resolve, reject)
} catch (err: any) {
reject(err)
}
if (this.doneCallback) {
this.doneCallback()
}
}
},
)
}

then<TResult1 = T, TResult2 = never>(
onfulfilled?:
| ((value: T) => TResult1 | PromiseLike<TResult1>)
| undefined
| null,
| null
| undefined,
onrejected?:
| ((reason: any) => TResult2 | PromiseLike<TResult2>)
| undefined
| null,
| null
| undefined,
): PromiseLike<TResult1 | TResult2> {
return new SimplePromise((resolve, reject) => {
if (this.status === 'pending') {
this.onFulfilledCallbacks.push(() => {
try {
const fulfilledFromLastPromise = onfulfilled?.(this.value)
if (fulfilledFromLastPromise && typeof fulfilledFromLastPromise === 'object' && 'then' in fulfilledFromLastPromise) {
fulfilledFromLastPromise.then(resolve, reject)
} else {
resolve(fulfilledFromLastPromise as TResult1)
}
} catch (err) {
reject(err)
const doneCallback = () => {
if (this.error) {
if (onrejected) {
SimplePromise.resolve(onrejected(this.error)).then(
resolve,
reject,
)
}
})
this.onRejectedCallbacks.push(() => {
try {
const rejectedFromLastPromise = onrejected?.(this.value)
if (rejectedFromLastPromise && typeof rejectedFromLastPromise === 'object' && 'then' in rejectedFromLastPromise) {
rejectedFromLastPromise.then(resolve, reject)
} else {
reject(rejectedFromLastPromise)
}
} catch (err) {
reject(err)
} else {
if (onfulfilled) {
SimplePromise.resolve(
onfulfilled(this.value as T),
).then(resolve, reject)
}
})
}

if (this.status === 'fulfilled') {
try {
const fulfilledFromLastPromise = onfulfilled?.(this.value)
if (fulfilledFromLastPromise && typeof fulfilledFromLastPromise === 'object' && 'then' in fulfilledFromLastPromise) {
fulfilledFromLastPromise.then(resolve, reject)
} else {
resolve(fulfilledFromLastPromise as TResult1)
}
} catch (err) {
reject(err)
}
}

if (this.status === 'rejected') {
try {
const rejectedFromLastPromise = onrejected?.(this.value)
if (rejectedFromLastPromise && typeof rejectedFromLastPromise === 'object' && 'then' in rejectedFromLastPromise) {
rejectedFromLastPromise.then(resolve, reject)
} else {
reject(rejectedFromLastPromise)
}
} catch (err) {
reject(err)
}
if (this.fulfilled) {
doneCallback()
} else {
this.doneCallback = doneCallback
}
})
}
Expand All @@ -116,18 +84,16 @@ 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(new CancellableError('cancelled promise')) : resolve(val)
hasCanceled_ ? reject({ isCanceled: true }) : resolve(val)
},
(error) => {
hasCanceled_ ? reject(new CancellableError('cancelled promise')) : reject(error)
hasCanceled_ ? reject({ isCanceled: true }) : reject(error)
},
)
})
Expand Down

0 comments on commit 8a8f8d3

Please sign in to comment.