feat(disposable/deduped): works with sync factories

This commit is contained in:
Julien Fontanet 2021-03-03 17:42:28 +01:00
parent f2d4fdd4d2
commit 43aad3d117
4 changed files with 46 additions and 21 deletions

View File

@ -24,8 +24,6 @@ Creates a new function that wraps `fn` and instead of creating a new disposables
Those copies contains the same value and can be disposed independently, the source disposable will only be disposed when all copies are disposed. Those copies contains the same value and can be disposed independently, the source disposable will only be disposed when all copies are disposed.
`fn` should return a promise containing a disposable.
`keyFn` is called with the same context and arguments as the wrapping function and must returns an array of keys which will be used to identify which disposables should be grouped together. `keyFn` is called with the same context and arguments as the wrapping function and must returns an array of keys which will be used to identify which disposables should be grouped together.
```js ```js

View File

@ -6,8 +6,6 @@ Creates a new function that wraps `fn` and instead of creating a new disposables
Those copies contains the same value and can be disposed independently, the source disposable will only be disposed when all copies are disposed. Those copies contains the same value and can be disposed independently, the source disposable will only be disposed when all copies are disposed.
`fn` should return a promise containing a disposable.
`keyFn` is called with the same context and arguments as the wrapping function and must returns an array of keys which will be used to identify which disposables should be grouped together. `keyFn` is called with the same context and arguments as the wrapping function and must returns an array of keys which will be used to identify which disposables should be grouped together.
```js ```js

View File

@ -1,8 +1,8 @@
const ensureArray = require('ensure-array') const ensureArray = require('ensure-array')
const { MultiKeyMap } = require('@vates/multi-key-map') const { MultiKeyMap } = require('@vates/multi-key-map')
function State(pGetNewDisposable) { function State(factory) {
this.pGetNewDisposable = pGetNewDisposable this.factory = factory
this.users = 0 this.users = 0
} }
@ -15,29 +15,38 @@ exports.deduped = (factory, keyFn = (...args) => args) =>
const keys = ensureArray(keyFn.apply(this, arguments)) const keys = ensureArray(keyFn.apply(this, arguments))
let state = states.get(keys) let state = states.get(keys)
if (state === undefined) { if (state === undefined) {
const pDisposable = factory.apply(this, arguments) const result = factory.apply(this, arguments)
pDisposable.catch(() => {
states.delete(keys)
})
state = new State( const createFactory = ({ value, dispose }) => {
pDisposable.then(({ value, dispose }) => { const wrapper = {
const disposeWrapper = () => { dispose() {
if (--state.users === 0) { if (--state.users === 0) {
states.delete(keys) states.delete(keys)
return dispose() return dispose()
} }
} },
return () => ({ value,
dispose: disposeWrapper, }
value,
}) return () => {
++state.users
return wrapper
}
}
if (typeof result.then !== 'function') {
state = new State(createFactory(result))
} else {
result.catch(() => {
states.delete(keys)
}) })
) const pFactory = result.then(createFactory)
state = new State(() => pFactory.then(call))
}
states.set(keys, state) states.set(keys, state)
} }
++state.users return state.factory()
return state.pGetNewDisposable.then(call)
} }
})() })()

View File

@ -40,4 +40,24 @@ describe('deduped()', () => {
expect(dispose).toHaveBeenCalledTimes(1) expect(dispose).toHaveBeenCalledTimes(1)
}) })
it('works with sync factory', () => {
const value = {}
const dispose = jest.fn()
const dedupedGetResource = deduped(() => ({ value, dispose }))
const d1 = dedupedGetResource()
expect(d1.value).toBe(value)
const d2 = dedupedGetResource()
expect(d2.value).toBe(value)
d1.dispose()
expect(dispose).not.toHaveBeenCalled()
d2.dispose()
expect(dispose).toHaveBeenCalledTimes(1)
})
}) })