fix: 🐛 also wait for loaders added while loading

This commit is contained in:
Christian Kaisermann 2020-05-31 12:50:17 -03:00
parent 1148b72c7d
commit e560514b1d
2 changed files with 61 additions and 25 deletions

View File

@ -7,24 +7,28 @@ import {
import { getRelatedLocalesOf } from '../stores/locale'
type Queue = Set<MessagesLoader>
const loaderQueue: Record<string, Queue> = {}
const queue: Record<string, Queue> = {}
export function resetQueues() {
Object.keys(loaderQueue).forEach(key => {
delete loaderQueue[key]
Object.keys(queue).forEach(key => {
delete queue[key]
})
}
function createLocaleQueue(locale: string) {
loaderQueue[locale] = new Set()
queue[locale] = new Set()
}
function removeLocaleFromQueue(locale: string) {
delete loaderQueue[locale]
function removeLoaderFromQueue(locale: string, loader: MessagesLoader) {
queue[locale].delete(loader)
if (queue[locale].size === 0) {
delete queue[locale]
}
}
function getLocaleQueue(locale: string) {
return loaderQueue[locale]
return queue[locale]
}
function getLocalesQueues(locale: string) {
@ -40,33 +44,46 @@ function getLocalesQueues(locale: string) {
export function hasLocaleQueue(locale: string) {
return getRelatedLocalesOf(locale)
.reverse()
.some(getLocaleQueue)
.some(locale => getLocaleQueue(locale)?.size)
}
const activeLocaleFlushes: { [key: string]: Promise<void> } = {}
export function flush(locale: string) {
if (!hasLocaleQueue(locale)) return
if (locale in activeLocaleFlushes) return activeLocaleFlushes[locale]
function loadLocaleQueue(locale: string, queue: MessagesLoader[]) {
const allLoadersPromise = Promise.all(
queue.map(loader => {
// todo: maybe don't just remove, but add to a `loading` set?
removeLoaderFromQueue(locale, loader)
return loader().then(partial => partial.default || partial)
})
)
return allLoadersPromise.then(partials => addMessages(locale, ...partials))
}
const activeFlushes: { [key: string]: Promise<void> } = {}
export function flush(locale: string): Promise<void> {
if (!hasLocaleQueue(locale)) {
if (locale in activeFlushes) {
return activeFlushes[locale]
}
return
}
// get queue of XX-YY and XX locales
const queues = getLocalesQueues(locale)
// istanbul ignore if
if (queues.length === 0) return
// TODO what happens if some loader fails
activeLocaleFlushes[locale] = Promise.all(
queues.map(([locale, queue]) => {
return Promise.all(queue.map(loader => loader())).then(partials => {
removeLocaleFromQueue(locale)
partials = partials.map(partial => partial.default || partial)
addMessages(locale, ...partials)
})
})
// todo: what happens if some loader fails?
activeFlushes[locale] = Promise.all(
queues.map(([locale, queue]) => loadLocaleQueue(locale, queue))
).then(() => {
delete activeLocaleFlushes[locale]
if (hasLocaleQueue(locale)) {
return flush(locale)
}
delete activeFlushes[locale]
})
return activeLocaleFlushes[locale]
return activeFlushes[locale]
}
export function registerLocaleLoader(locale: string, loader: MessagesLoader) {

View File

@ -50,3 +50,22 @@ test('consecutive flushes return the same promise', async () => {
expect(flushB).toStrictEqual(flushA)
expect(flushC).toStrictEqual(flushA)
})
test('waits for loaders added while already flushing', async () => {
registerLocaleLoader(
'en',
() => new Promise(res => setTimeout(() => res({ foo: 'foo' }), 300))
)
const flushPromise = flush('en')
registerLocaleLoader(
'en',
() => new Promise(res => setTimeout(() => res({ bar: 'bar' })))
)
await flushPromise
expect(getMessageFromDictionary('en', 'foo')).toBe('foo')
expect(getMessageFromDictionary('en', 'bar')).toBe('bar')
})