Skip to content

Commit e7cdebf

Browse files
authored
fix: support non-native promises (#3068)
1 parent b5ca60c commit e7cdebf

File tree

5 files changed

+18
-13
lines changed

5 files changed

+18
-13
lines changed

src/react/useAtomValue.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,6 @@ export function useAtomValue<Value>(atom: Atom<Value>, options?: Options) {
169169
}, [store, atom, delay, promiseStatus])
170170

171171
useDebugValue(value)
172-
// The use of isPromiseLike is to be consistent with `use` type.
173-
// `instanceof Promise` actually works fine in this case.
174172
if (isPromiseLike(value)) {
175173
const promise = createContinuablePromise(value, () => store.get(atom))
176174
if (promiseStatus) {

src/vanilla/utils/atomWithObservable.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { atom } from '../../vanilla.ts'
22
import type { Atom, Getter, WritableAtom } from '../../vanilla.ts'
33

4+
const isPromiseLike = (x: unknown): x is PromiseLike<unknown> =>
5+
typeof (x as any)?.then === 'function'
6+
47
type Timeout = ReturnType<typeof setTimeout>
58
type AnyError = unknown
69

@@ -165,7 +168,7 @@ export function atomWithObservable<Data>(
165168
(get) => {
166169
const [resultAtom] = get(observableResultAtom)
167170
const result = get(resultAtom)
168-
if (result instanceof Promise) {
171+
if (isPromiseLike(result)) {
169172
return result.then(returnResultData)
170173
}
171174
return returnResultData(result)

src/vanilla/utils/atomWithStorage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ export function atomWithStorage<Value>(
258258
set(baseAtom, initialValue)
259259
return storage.removeItem(key)
260260
}
261-
if (nextValue instanceof Promise) {
261+
if (isPromiseLike(nextValue)) {
262262
return nextValue.then((resolvedValue) => {
263263
set(baseAtom, resolvedValue)
264264
return storage.setItem(key, resolvedValue)

src/vanilla/utils/loadable.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ const cache1 = new WeakMap()
55
const memo1 = <T>(create: () => T, dep1: object): T =>
66
(cache1.has(dep1) ? cache1 : cache1.set(dep1, create())).get(dep1)
77

8-
const isPromise = <Value>(x: unknown): x is Promise<Awaited<Value>> =>
9-
x instanceof Promise
8+
const isPromiseLike = <Value>(p: unknown): p is PromiseLike<Awaited<Value>> =>
9+
typeof (p as any)?.then === 'function'
1010

1111
export type Loadable<Value> =
1212
| { state: 'loading' }
@@ -18,7 +18,7 @@ const LOADING: Loadable<unknown> = { state: 'loading' }
1818
export function loadable<Value>(anAtom: Atom<Value>): Atom<Loadable<Value>> {
1919
return memo1(() => {
2020
const loadableCache = new WeakMap<
21-
Promise<Awaited<Value>>,
21+
PromiseLike<Awaited<Value>>,
2222
Loadable<Value>
2323
>()
2424
const refreshAtom = atom(0)
@@ -36,7 +36,7 @@ export function loadable<Value>(anAtom: Atom<Value>): Atom<Loadable<Value>> {
3636
} catch (error) {
3737
return { state: 'hasError', error } as Loadable<Value>
3838
}
39-
if (!isPromise<Value>(value)) {
39+
if (!isPromiseLike<Value>(value)) {
4040
return { state: 'hasData', data: value } as Loadable<Value>
4141
}
4242
const promise = value

src/vanilla/utils/unwrap.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ const memo2 = <T>(create: () => T, dep1: object, dep2: object): T => {
99
return getCached(create, cache2, dep2)
1010
}
1111

12-
const isPromise = (x: unknown): x is Promise<unknown> => x instanceof Promise
12+
const isPromiseLike = (p: unknown): p is PromiseLike<unknown> =>
13+
typeof (p as any)?.then === 'function'
1314

1415
const defaultFallback = () => undefined
1516

@@ -37,12 +38,15 @@ export function unwrap<Value, Args extends unknown[], Result, PendingValue>(
3738
) {
3839
return memo2(
3940
() => {
40-
type PromiseAndValue = { readonly p?: Promise<unknown> } & (
41+
type PromiseAndValue = { readonly p?: PromiseLike<unknown> } & (
4142
| { readonly v: Awaited<Value> }
4243
| { readonly f: PendingValue; readonly v?: Awaited<Value> }
4344
)
44-
const promiseErrorCache = new WeakMap<Promise<unknown>, unknown>()
45-
const promiseResultCache = new WeakMap<Promise<unknown>, Awaited<Value>>()
45+
const promiseErrorCache = new WeakMap<PromiseLike<unknown>, unknown>()
46+
const promiseResultCache = new WeakMap<
47+
PromiseLike<unknown>,
48+
Awaited<Value>
49+
>()
4650
const refreshAtom = atom(0)
4751

4852
if (import.meta.env?.MODE !== 'production') {
@@ -56,7 +60,7 @@ export function unwrap<Value, Args extends unknown[], Result, PendingValue>(
5660
get(refreshAtom)
5761
const prev = get(promiseAndValueAtom) as PromiseAndValue | undefined
5862
const promise = get(anAtom)
59-
if (!isPromise(promise)) {
63+
if (!isPromiseLike(promise)) {
6064
return { v: promise as Awaited<Value> }
6165
}
6266
if (promise !== prev?.p) {

0 commit comments

Comments
 (0)