@@ -176,54 +176,42 @@ const returnAtomValue = <Value>(atomState: AtomState<Value>): Value => {
176
176
}
177
177
178
178
//
179
- // Cancelable Promise
180
- // TODO(daishi): revisit this implementation
179
+ // Abortable Promise
181
180
//
182
181
183
- type CancelHandler = ( nextValue : unknown ) => void
184
- type PromiseState = [ cancelHandlers : Set < CancelHandler > , settled : boolean ]
185
-
186
- const PROMISE_STATE = Symbol ( )
187
-
188
- const getPromiseState = < T > (
189
- promise : PromiseLike < T > ,
190
- ) : PromiseState | undefined => ( promise as any ) [ PROMISE_STATE ]
182
+ const promiseStateMap : WeakMap <
183
+ PromiseLike < unknown > ,
184
+ [ pending : boolean , abortHandlers : Set < ( ) => void > ]
185
+ > = new WeakMap ( )
191
186
192
187
const isPendingPromise = ( value : unknown ) : value is PromiseLike < unknown > =>
193
- isPromiseLike ( value ) && ! getPromiseState ( value ) ?. [ 1 ]
188
+ isPromiseLike ( value ) && ! ! promiseStateMap . get ( value as never ) ?. [ 0 ]
194
189
195
- const cancelPromise = < T > (
196
- promise : PromiseLike < T > ,
197
- nextValue : unknown ,
198
- ) : void => {
199
- const promiseState = getPromiseState ( promise )
200
- if ( promiseState ) {
201
- promiseState [ 1 ] = true
202
- promiseState [ 0 ] . forEach ( ( fn ) => fn ( nextValue ) )
203
- } else if ( import . meta. env ?. MODE !== 'production' ) {
204
- throw new Error ( '[Bug] cancelable promise not found' )
190
+ const abortPromise = < T > ( promise : PromiseLike < T > ) : void => {
191
+ const promiseState = promiseStateMap . get ( promise )
192
+ if ( promiseState ?. [ 0 ] ) {
193
+ promiseState [ 0 ] = false
194
+ promiseState [ 1 ] . forEach ( ( fn ) => fn ( ) )
205
195
}
206
196
}
207
197
208
- const patchPromiseForCancelability = < T > ( promise : PromiseLike < T > ) : void => {
209
- if ( getPromiseState ( promise ) ) {
210
- // already patched
211
- return
212
- }
213
- const promiseState : PromiseState = [ new Set ( ) , false ]
214
- ; ( promise as any ) [ PROMISE_STATE ] = promiseState
215
- const settle = ( ) => {
216
- promiseState [ 1 ] = true
217
- }
218
- promise . then ( settle , settle )
219
- ; ( promise as { onCancel ?: ( fn : CancelHandler ) => void } ) . onCancel = ( fn ) => {
220
- promiseState [ 0 ] . add ( fn )
198
+ const registerAbortHandler = < T > (
199
+ promise : PromiseLike < T > ,
200
+ abortHandler : ( ) => void ,
201
+ ) : void => {
202
+ let promiseState = promiseStateMap . get ( promise )
203
+ if ( ! promiseState ) {
204
+ promiseState = [ true , new Set ( ) ]
205
+ promiseStateMap . set ( promise , promiseState )
206
+ const settle = ( ) => {
207
+ promiseState ! [ 0 ] = false
208
+ }
209
+ promise . then ( settle , settle )
221
210
}
211
+ promiseState [ 1 ] . add ( abortHandler )
222
212
}
223
213
224
- const isPromiseLike = (
225
- p : unknown ,
226
- ) : p is PromiseLike < unknown > & { onCancel ?: ( fn : CancelHandler ) => void } =>
214
+ const isPromiseLike = ( p : unknown ) : p is PromiseLike < unknown > =>
227
215
typeof ( p as any ) ?. then === 'function'
228
216
229
217
const addPendingPromiseToDependency = (
@@ -253,9 +241,7 @@ const setAtomStateValueOrPromise = (
253
241
const atomState = ensureAtomState ( atom )
254
242
const hasPrevValue = 'v' in atomState
255
243
const prevValue = atomState . v
256
- const pendingPromise = isPendingPromise ( atomState . v ) ? atomState . v : null
257
244
if ( isPromiseLike ( valueOrPromise ) ) {
258
- patchPromiseForCancelability ( valueOrPromise )
259
245
for ( const a of atomState . d . keys ( ) ) {
260
246
addPendingPromiseToDependency ( atom , valueOrPromise , ensureAtomState ( a ) )
261
247
}
@@ -264,8 +250,8 @@ const setAtomStateValueOrPromise = (
264
250
delete atomState . e
265
251
if ( ! hasPrevValue || ! Object . is ( prevValue , atomState . v ) ) {
266
252
++ atomState . n
267
- if ( pendingPromise ) {
268
- cancelPromise ( pendingPromise , valueOrPromise )
253
+ if ( isPromiseLike ( prevValue ) ) {
254
+ abortPromise ( prevValue )
269
255
}
270
256
}
271
257
}
@@ -662,7 +648,7 @@ const buildStore = (
662
648
const valueOrPromise = atomRead ( atom , getter , options as never )
663
649
setAtomStateValueOrPromise ( atom , valueOrPromise , ensureAtomState ! )
664
650
if ( isPromiseLike ( valueOrPromise ) ) {
665
- valueOrPromise . onCancel ?. ( ( ) => controller ?. abort ( ) )
651
+ registerAbortHandler ( valueOrPromise , ( ) => controller ?. abort ( ) )
666
652
valueOrPromise . then (
667
653
mountDependenciesIfAsync ,
668
654
mountDependenciesIfAsync ,
@@ -931,12 +917,12 @@ export const INTERNAL_isActuallyWritableAtom: typeof isActuallyWritableAtom =
931
917
export const INTERNAL_isAtomStateInitialized : typeof isAtomStateInitialized =
932
918
isAtomStateInitialized
933
919
export const INTERNAL_returnAtomValue : typeof returnAtomValue = returnAtomValue
934
- export const INTERNAL_getPromiseState : typeof getPromiseState = getPromiseState
920
+ export const INTERNAL_promiseStateMap : typeof promiseStateMap = promiseStateMap
935
921
export const INTERNAL_isPendingPromise : typeof isPendingPromise =
936
922
isPendingPromise
937
- export const INTERNAL_cancelPromise : typeof cancelPromise = cancelPromise
938
- export const INTERNAL_patchPromiseForCancelability : typeof patchPromiseForCancelability =
939
- patchPromiseForCancelability
923
+ export const INTERNAL_abortPromise : typeof abortPromise = abortPromise
924
+ export const INTERNAL_registerAbortHandler : typeof registerAbortHandler =
925
+ registerAbortHandler
940
926
export const INTERNAL_isPromiseLike : typeof isPromiseLike = isPromiseLike
941
927
export const INTERNAL_addPendingPromiseToDependency : typeof addPendingPromiseToDependency =
942
928
addPendingPromiseToDependency
0 commit comments