mirror of
https://github.com/trezor/trezor-suite.git
synced 2026-02-25 11:12:17 +01:00
feat(utils): add lockId to getSynchronize
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* Ensures that all async actions passed to the returned function are called
|
||||
* immediately one after another, without interfering with each other.
|
||||
* Optionally, it also takes `lockId` param, in which case only actions with
|
||||
* the same lock id are blocking each other.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
@@ -8,23 +10,28 @@
|
||||
* const synchronize = getSynchronize();
|
||||
* synchronize(() => asyncAction1());
|
||||
* synchronize(() => asyncAction2());
|
||||
* synchronize(() => asyncAction3(), 'differentLockId');
|
||||
* ```
|
||||
*/
|
||||
export const getSynchronize = () => {
|
||||
let lock: Promise<unknown> | undefined;
|
||||
const DEFAULT_ID = Symbol();
|
||||
const locks: Record<keyof any, Promise<unknown>> = {};
|
||||
|
||||
return <T>(action: () => T): T extends Promise<unknown> ? T : Promise<T> => {
|
||||
const newLock = (lock ?? Promise.resolve())
|
||||
return <T>(
|
||||
action: () => T,
|
||||
lockId: keyof any = DEFAULT_ID,
|
||||
): T extends Promise<unknown> ? T : Promise<T> => {
|
||||
const newLock = (locks[lockId] ?? Promise.resolve())
|
||||
.catch(() => {})
|
||||
.then(action)
|
||||
.finally(() => {
|
||||
if (lock === newLock) {
|
||||
lock = undefined;
|
||||
if (locks[lockId] === newLock) {
|
||||
delete locks[lockId];
|
||||
}
|
||||
});
|
||||
lock = newLock;
|
||||
locks[lockId] = newLock;
|
||||
|
||||
return lock as any;
|
||||
return newLock as any;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -63,9 +63,60 @@ describe('getSynchronize', () => {
|
||||
synchronize(() =>
|
||||
sequence(['a', 3]).then(() => {
|
||||
// 'c' registers after 'a' ended and while 'b' is running
|
||||
delay(2).then(() => synchronize(() => sequence(['c', 3])));
|
||||
delay(2).then(() => synchronize(() => sequence(['c', 3])).then(done));
|
||||
}),
|
||||
);
|
||||
synchronize(() => sequence(['b', 8]).then(done));
|
||||
synchronize(() => sequence(['b', 8]));
|
||||
});
|
||||
|
||||
it('with keys', async () => {
|
||||
let state1: any, state2: any;
|
||||
|
||||
await Promise.all([
|
||||
synchronize(async () => {
|
||||
state1 = 'a';
|
||||
await delay(3);
|
||||
expect(state1).toBe('a');
|
||||
|
||||
state1 = 'b';
|
||||
await delay(9);
|
||||
expect(state1).toBe('b');
|
||||
|
||||
state1 = 'c';
|
||||
await delay(3);
|
||||
expect(state1).toBe('c');
|
||||
}, 'lock1'),
|
||||
synchronize(async () => {
|
||||
expect(state1).toBe('a');
|
||||
|
||||
state2 = 'g';
|
||||
await delay(8);
|
||||
expect(state2).toBe('g');
|
||||
expect(state1).toBe('b');
|
||||
|
||||
state2 = 'h';
|
||||
await delay(11);
|
||||
expect(state2).toBe('h');
|
||||
expect(state1).toBe('d');
|
||||
|
||||
state2 = 'i';
|
||||
await delay(12);
|
||||
expect(state2).toBe('i');
|
||||
expect(state1).toBe('f');
|
||||
}, 'lock2'),
|
||||
synchronize(async () => {
|
||||
state1 = 'd';
|
||||
await delay(8);
|
||||
expect(state1).toBe('d');
|
||||
|
||||
state1 = 'e';
|
||||
await delay(4);
|
||||
expect(state1).toBe('e');
|
||||
|
||||
state1 = 'f';
|
||||
await delay(2);
|
||||
expect(state1).toBe('f');
|
||||
}, 'lock1'),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user