Match-id-30ebfb9de74da58e87335f3d031ca098a506122c
This commit is contained in:
commit
b415fd3346
|
@ -12,5 +12,6 @@
|
|||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"dependencies": {}
|
||||
"dependencies": {},
|
||||
"types": "@types/index.d.ts"
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ export function createStore(reducer: Reducer, preloadedState?: any, enhancers?):
|
|||
},
|
||||
},
|
||||
options: {
|
||||
suppressHooks: true,
|
||||
reduxAdapter: true,
|
||||
},
|
||||
})();
|
||||
|
||||
|
|
|
@ -38,7 +38,9 @@ export function createProxy(rawObj: any, hookObserver = true): any {
|
|||
|
||||
// 创建Proxy
|
||||
let proxyObj;
|
||||
if (isArray(rawObj)) {
|
||||
if (!hookObserver) {
|
||||
proxyObj = createObjectProxy(rawObj,true);
|
||||
} else if (isArray(rawObj)) {
|
||||
// 数组
|
||||
proxyObj = createArrayProxy(rawObj as []);
|
||||
} else if (isCollection(rawObj)) {
|
||||
|
|
|
@ -2,16 +2,16 @@ import { isSame } from '../../CommonUtils';
|
|||
import { createProxy, getObserver, hookObserverMap } from '../ProxyHandler';
|
||||
import { OBSERVER_KEY } from '../../Constants';
|
||||
|
||||
export function createObjectProxy<T extends object>(rawObj: T): ProxyHandler<T> {
|
||||
export function createObjectProxy<T extends object>(rawObj: T, singleLevel = false): ProxyHandler<T> {
|
||||
const proxy = new Proxy(rawObj, {
|
||||
get,
|
||||
get: (...args) => get(...args, singleLevel),
|
||||
set,
|
||||
});
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
export function get(rawObj: object, key: string | symbol, receiver: any): any {
|
||||
export function get(rawObj: object, key: string | symbol, receiver: any, singleLevel = false): any {
|
||||
// The observer object of symbol ('_horizonObserver') cannot be accessed from Proxy to prevent errors caused by clonedeep.
|
||||
if (key === OBSERVER_KEY) {
|
||||
return undefined;
|
||||
|
@ -34,7 +34,7 @@ export function get(rawObj: object, key: string | symbol, receiver: any): any {
|
|||
// 对于prototype不做代理
|
||||
if (key !== 'prototype') {
|
||||
// 对于value也需要进一步代理
|
||||
const valProxy = createProxy(value, hookObserverMap.get(rawObj));
|
||||
const valProxy = singleLevel ? value : createProxy(value, hookObserverMap.get(rawObj));
|
||||
|
||||
return valProxy;
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ export function createStore<S extends object, A extends UserActions<S>, C extend
|
|||
throw new Error('store obj must be pure object');
|
||||
}
|
||||
|
||||
const proxyObj = createProxy(config.state, !config.options?.suppressHooks);
|
||||
const proxyObj = createProxy(config.state, !config.options?.reduxAdapter);
|
||||
|
||||
proxyObj.$pending = false;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//@ts-ignore
|
||||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as Horizon from '../../../../libs/horizon';
|
||||
import { createStore } from '../../../../libs/horizon/src/horizonx/store/StoreHandler';
|
||||
import { triggerClickEvent } from '../../jest/commonComponents';
|
||||
import { describe, beforeEach, afterEach, it, expect } from '@jest/globals';
|
||||
|
@ -8,40 +8,35 @@ const { unmountComponentAtNode } = Horizon;
|
|||
|
||||
function postpone(timer, func) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(function() {
|
||||
window.setTimeout(function () {
|
||||
console.log('resolving postpone');
|
||||
resolve(func());
|
||||
}, timer);
|
||||
});
|
||||
}
|
||||
|
||||
describe('Asynchronous functions', () => {
|
||||
let container: HTMLElement | null = null;
|
||||
|
||||
const COUNTER_ID = 'counter';
|
||||
const TOGGLE_ID = 'toggle';
|
||||
const TOGGLE_FAST_ID = 'toggleFast';
|
||||
const RESULT_ID = 'result';
|
||||
|
||||
let useAsyncCounter;
|
||||
|
||||
beforeEach(() => {
|
||||
useAsyncCounter = createStore({
|
||||
describe('Asynchronous store', () => {
|
||||
const useAsyncCounter = createStore({
|
||||
state: {
|
||||
counter: 0,
|
||||
check: false,
|
||||
},
|
||||
actions: {
|
||||
increment: function(state) {
|
||||
increment: function (state) {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
window.setTimeout(() => {
|
||||
state.counter++;
|
||||
resolve(true);
|
||||
}, 100);
|
||||
}, 10);
|
||||
});
|
||||
},
|
||||
toggle: function(state) {
|
||||
toggle: function (state) {
|
||||
state.check = !state.check;
|
||||
},
|
||||
reset: function (state) {
|
||||
state.check = false;
|
||||
state.counter = 0;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
value: state => {
|
||||
|
@ -49,118 +44,54 @@ describe('Asynchronous functions', () => {
|
|||
},
|
||||
},
|
||||
});
|
||||
container = document.createElement('div');
|
||||
document.body.appendChild(container);
|
||||
|
||||
beforeEach(() => {
|
||||
useAsyncCounter().reset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
unmountComponentAtNode(container);
|
||||
container?.remove();
|
||||
container = null;
|
||||
});
|
||||
|
||||
it('Should wait for async actions', async () => {
|
||||
// @ts-ignore
|
||||
jest.useRealTimers();
|
||||
let globalStore;
|
||||
|
||||
function App() {
|
||||
const store = useAsyncCounter();
|
||||
globalStore = store;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p id={RESULT_ID}>{store.value}</p>
|
||||
<button onClick={store.$queue.increment} id={COUNTER_ID}>
|
||||
add 1
|
||||
</button>
|
||||
<button onClick={store.$queue.toggle} id={TOGGLE_ID}>
|
||||
slow toggle
|
||||
</button>
|
||||
<button onClick={store.toggle} id={TOGGLE_FAST_ID}>
|
||||
fast toggle
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Horizon.render(<App />, container);
|
||||
|
||||
// initial state
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false0');
|
||||
|
||||
// slow toggle has nothing to wait for, it is resolved immediately
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, TOGGLE_ID);
|
||||
});
|
||||
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('true0');
|
||||
|
||||
// counter increment is slow. slow toggle waits for result
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, COUNTER_ID);
|
||||
});
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, TOGGLE_ID);
|
||||
});
|
||||
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('true0');
|
||||
|
||||
// fast toggle does not wait for counter and it is resolved immediately
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, TOGGLE_FAST_ID);
|
||||
});
|
||||
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false0');
|
||||
|
||||
// at 150ms counter increment will be resolved and slow toggle immediately after
|
||||
const t150 = postpone(150, () => {
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('true1');
|
||||
});
|
||||
|
||||
// before that, two more actions are added to queue - another counter and slow toggle
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, COUNTER_ID);
|
||||
});
|
||||
Horizon.act(() => {
|
||||
triggerClickEvent(container, TOGGLE_ID);
|
||||
});
|
||||
|
||||
// at 250ms they should be already resolved
|
||||
const t250 = postpone(250, () => {
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false2');
|
||||
});
|
||||
|
||||
await Promise.all([t150, t250]);
|
||||
});
|
||||
|
||||
it('call async action by then', async () => {
|
||||
// @ts-ignore
|
||||
it('should return promise when queued function is called', () => {
|
||||
jest.useFakeTimers();
|
||||
let globalStore;
|
||||
|
||||
function App() {
|
||||
const store = useAsyncCounter();
|
||||
globalStore = store;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p id={RESULT_ID}>{store.value}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Horizon.render(<App />, container);
|
||||
|
||||
// call async action by then
|
||||
globalStore.$queue.increment().then(() => {
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false1');
|
||||
return new Promise(resolve => {
|
||||
store.$queue.increment().then(() => {
|
||||
expect(store.counter == 1);
|
||||
resolve(true);
|
||||
});
|
||||
|
||||
expect(document.getElementById(RESULT_ID)?.innerHTML).toBe('false0');
|
||||
|
||||
// past 150 ms
|
||||
// @ts-ignore
|
||||
jest.advanceTimersByTime(150);
|
||||
});
|
||||
});
|
||||
|
||||
it('should queue async functions', () => {
|
||||
jest.useFakeTimers();
|
||||
return new Promise(resolve => {
|
||||
const store = useAsyncCounter();
|
||||
|
||||
//initial value
|
||||
expect(store.value).toBe('false0');
|
||||
|
||||
// no blocking action action
|
||||
store.$queue.toggle();
|
||||
expect(store.value).toBe('true0');
|
||||
|
||||
// store is not updated before blocking action is resolved
|
||||
store.$queue.increment();
|
||||
const togglePromise = store.$queue.toggle();
|
||||
expect(store.value).toBe('true0');
|
||||
|
||||
// fast action is resolved immediatelly
|
||||
store.toggle();
|
||||
expect(store.value).toBe('false0');
|
||||
|
||||
// queued action waits for blocking action to resolve
|
||||
togglePromise.then(() => {
|
||||
expect(store.value).toBe('true1');
|
||||
resolve();
|
||||
});
|
||||
|
||||
jest.advanceTimersByTime(150);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
export const ActionType = {
|
||||
Pending: 'PENDING',
|
||||
Fulfilled: 'FULFILLED',
|
||||
Rejected: 'REJECTED',
|
||||
};
|
||||
|
||||
export const promise = store => next => action => {
|
||||
//let result = next(action);
|
||||
store._horizonXstore.$queue.dispatch(action);
|
||||
return result;
|
||||
};
|
Loading…
Reference in New Issue