Skip to content

Commit

Permalink
Do not emit changed event from async-state if same value is set
Browse files Browse the repository at this point in the history
  • Loading branch information
yuriyyakym committed Dec 6, 2023
1 parent 4a55301 commit d93ac5e
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 4 deletions.
13 changes: 11 additions & 2 deletions src/async-state/async-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const asyncState = <T>(
let newValue = isFunction(nextValueOrResolver)
? await nextValueOrResolver(value)
: await nextValueOrResolver;
const isChanged = !Object.is(newValue, value);

if (currentPendingVersion !== lastPendingVersion) {
events.ignored.emit({ value: newValue, version: currentPendingVersion });
Expand All @@ -57,19 +58,27 @@ const asyncState = <T>(
status = AsyncStatus.FULFILLED;

events.fulfilled.emit(newValue);
events.changed.emit(value);

if (isChanged) {
events.changed.emit(value);
}
} catch (e) {
if (currentPendingVersion !== lastPendingVersion) {
events.ignored.emit({ error: e, version: currentPendingVersion });
return;
}

const isChanged = !Object.is(e, error);

error = e;
value = undefined;
status = AsyncStatus.REJECTED;

events.rejected.emit(error);
events.changed.emit(value);

if (isChanged) {
events.changed.emit(value);
}
}

version = lastPendingVersion;
Expand Down
22 changes: 20 additions & 2 deletions tests/async-state.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from 'vitest';
import { expect, test, vi } from 'vitest';

import { asyncState, AsyncStatus, delay, SystemTag } from '../src';
import { asyncState, AsyncStatus, delay, flush, scenario, SystemTag } from '../src';

test('resolves immediately if non-async value is set', async () => {
const greeting = asyncState<string>('Async state');
Expand Down Expand Up @@ -80,6 +80,24 @@ test('ignores previous rejected promises if newer promise was set', async () =>
expect(state.get()).toBe(4);
});

test('does not emit `changed` event if same value is set', async () => {
const onChange = vi.fn();
const state = asyncState('test');

scenario(state.events.changed, onChange);

await flush();
expect(state.get()).toEqual('test');
expect(onChange).toBeCalledTimes(0);
await state.set(Promise.resolve('Awai'));
expect(state.get()).toEqual('Awai');
expect(onChange).toBeCalledTimes(1);
await state.set(delay(10).then(() => 'Awai'));
await delay(20);
expect(state.get()).toEqual('Awai');
expect(onChange).toBeCalledTimes(1);
});

test('emits `ignored` event if outdated version promise is settled', async () => {
const state = asyncState(delay(5).then(() => 'a'));
state.set(delay(5).then(() => 'b'));
Expand Down

0 comments on commit d93ac5e

Please sign in to comment.