diff --git a/lib/storage/providers/LocalForage.js b/lib/storage/providers/LocalForage.js index 3a8c4634a..7c254be8d 100644 --- a/lib/storage/providers/LocalForage.js +++ b/lib/storage/providers/LocalForage.js @@ -12,6 +12,33 @@ localforage.config({ name: 'OnyxDB' }); +const writeQueue = []; +let isQueueProcessing = false; + +/** + * Writing very quickly to IndexedDB causes performance issues and can lock up the page and lead to jank. + * So, we are slowing this process down by waiting until one write is complete before moving on + * to the next. + */ +function processNextWrite() { + if (isQueueProcessing || writeQueue.length === 0) { + return; + } + + isQueueProcessing = true; + + const { + key, value, resolve, reject, + } = writeQueue.shift(); + localforage.setItem(key, value) + .then(resolve) + .catch(reject) + .finally(() => { + isQueueProcessing = false; + processNextWrite(); + }); +} + const provider = { /** * Get multiple key-value pairs for the give array of keys in a batch @@ -90,7 +117,14 @@ const provider = { * @param {*} value * @return {Promise} */ - setItem: localforage.setItem, + setItem: (key, value) => ( + new Promise((resolve, reject) => { + writeQueue.push({ + key, value, resolve, reject, + }); + processNextWrite(); + }) + ), }; export default provider; diff --git a/tests/unit/storage/providers/LocalForageProviderTest.js b/tests/unit/storage/providers/LocalForageProviderTest.js index 9a6ef1d15..1940a09a1 100644 --- a/tests/unit/storage/providers/LocalForageProviderTest.js +++ b/tests/unit/storage/providers/LocalForageProviderTest.js @@ -15,17 +15,21 @@ describe('storage/providers/LocalForage', () => { // For some reason fake timers cause promises to hang beforeAll(() => jest.useRealTimers()); - beforeEach(() => jest.resetAllMocks()); + beforeEach(() => { + jest.resetAllMocks(); + localforage.setItem = jest.fn(() => Promise.resolve()); + }); it('multiSet', () => { // Given multiple pairs to be saved in storage const pairs = SAMPLE_ITEMS.slice(); // When they are saved - StorageProvider.multiSet(pairs); - - // We expect a call to localForage.setItem for each pair - _.each(pairs, ([key, value]) => expect(localforage.setItem).toHaveBeenCalledWith(key, value)); + return StorageProvider.multiSet(pairs) + .then(() => { + // We expect a call to localForage.setItem for each pair + _.each(pairs, ([key, value]) => expect(localforage.setItem).toHaveBeenCalledWith(key, value)); + }); }); it('multiGet', () => {