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.
`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.
```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.
`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.
```js

View File

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

View File

@ -40,4 +40,24 @@ describe('deduped()', () => {
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)
})
})