Commit 4685aafc by Vladislav Lagunov

Merge branch 'master' of ssh://git.bitmaster.ru:34022/npm/common

parents d67d4f07 cbaa6040
...@@ -17,6 +17,8 @@ export type Eff<Error, Success> = ...@@ -17,6 +17,8 @@ export type Eff<Error, Success> =
| Apply<Error, Success> // { args: Eff<unknown>[], proj(...args): Either<Error, Success> } | Apply<Error, Success> // { args: Eff<unknown>[], proj(...args): Either<Error, Success> }
| Chain<Error, Success> // { first: Eff<unknown>, andThen(x: unknown): Eff<Error, Success> } | Chain<Error, Success> // { first: Eff<unknown>, andThen(x: unknown): Eff<Error, Success> }
| HasEffect<Error, Success> // { toEffect(): Eff<Error, Success> } | HasEffect<Error, Success> // { toEffect(): Eff<Error, Success> }
;
export type AnyEff = Eff<any, any>;
// Instance methods for `Eff` // Instance methods for `Eff`
...@@ -140,7 +142,7 @@ export function ap(): Eff<unknown, unknown> { ...@@ -140,7 +142,7 @@ export function ap(): Eff<unknown, unknown> {
} }
export function record<R extends Record<string, Eff<any, any>>>(rec: R): Eff<{ [K in keyof R]: R[K]['_Error'] }[keyof R], { [K in keyof R]: R[K]['_Success'] }> { export function record<R extends Record<string, AnyEff>>(rec: R): Eff<{ [K in keyof R]: R[K]['_Error'] }[keyof R], { [K in keyof R]: R[K]['_Success'] }> {
const keys = Object.keys(rec); const keys = Object.keys(rec);
return ap.apply(undefined, [...keys.map(k => rec[k]), (...values) => values.reduce((acc, v, idx) => (acc[keys[idx]] = v, acc), {})]); return ap.apply(undefined, [...keys.map(k => rec[k]), (...values) => values.reduce((acc, v, idx) => (acc[keys[idx]] = v, acc), {})]);
} }
...@@ -240,23 +242,27 @@ export function go<Error, Success>(effect: Eff<Error, Success>, onNext: (x: Eith ...@@ -240,23 +242,27 @@ export function go<Error, Success>(effect: Eff<Error, Success>, onNext: (x: Eith
} }
if (effect instanceof Chain) { if (effect instanceof Chain) {
const subscriptions: Array<Function|null> = []; const cancellers = new Map<AnyEff, Canceller>();
subscriptions.push(go(effect.first, result => { const handleEffect = (e: AnyEff) => {
const idx = subscriptions.length; const _onNext = result => {
subscriptions.push(go(effect.andThen(result), onNext, () => { if (e === effect.first) handleEffect(effect.andThen(result));
subscriptions[idx] = null; else onNext(result);
for (const unsub of subscriptions) if (unsub !== null) return; };
onComplete(); const _onComplete = () => {
})); if (!cancellers.has(e)) completedImmediately = true;
}, () => { cancellers.delete(e);
subscriptions[0] = null; if (cancellers.size === 0) onComplete();
for (const unsub of subscriptions) if (unsub !== null) return; };
onComplete(); let completedImmediately = false;
}));
const canceller = go(e, _onNext, _onComplete);
if (!completedImmediately) cancellers.set(e, canceller);
};
handleEffect(effect.first);
if (cancellers.size === 0) return noopFunc;
return () => subscriptions.forEach( return () => cancellers.forEach(canceller => canceller());
funOrNull => funOrNull ? funOrNull() : void 0
);
} }
if (effect instanceof Apply) { if (effect instanceof Apply) {
...@@ -355,6 +361,9 @@ export abstract class HasEffect<Error, Success> extends EffBase<Error, Success> ...@@ -355,6 +361,9 @@ export abstract class HasEffect<Error, Success> extends EffBase<Error, Success>
export const noop: Cmd<never> = new Batch([]); export const noop: Cmd<never> = new Batch([]);
export type Canceller = () => void;
export interface EffStatics { export interface EffStatics {
of: typeof of, of: typeof of,
success: typeof success, success: typeof success,
......
...@@ -12,10 +12,10 @@ Update.Do = Do; ...@@ -12,10 +12,10 @@ Update.Do = Do;
declare module "./" { declare module "./" {
interface UpdateStatic { interface UpdateStatic {
Do<State, Result>(iter: () => IterableIterator<Update<any, any>>): Update<State, Result>; Do<State, Result>(iter: () => IterableIterator<Update<Error, any, any>>): Update<Error, State, Result>;
} }
interface BoundStatics<State> { interface BoundStatics<Error, State> {
Do<Result>(iter: () => IterableIterator<Update<any, any>>): Update<State, Result>; Do<Result>(iter: () => IterableIterator<Update<Error, any, any>>): Update<Error, State, Result>;
} }
} }
......
...@@ -6,6 +6,8 @@ import * as http from '../../http'; ...@@ -6,6 +6,8 @@ import * as http from '../../http';
import { InputHTMLAttributes } from 'react'; import { InputHTMLAttributes } from 'react';
type Err = http.HttpError;
type Props = { type Props = {
}; };
...@@ -15,10 +17,10 @@ type State = { ...@@ -15,10 +17,10 @@ type State = {
response?: http.Response; response?: http.Response;
}; };
const U = Update.bind<State>(); const U = Update.bind<Err, State>();
class Widget extends Update.Component<Props, State> { class Widget extends Update.Component<Err, Props, State> {
state: State = { search: '', pending: false }; state: State = { search: '', pending: false };
handleNameChange: InputHTMLAttributes<HTMLInputElement>['onChange'] = (e) => this.setState({ search: e.target.value }); handleNameChange: InputHTMLAttributes<HTMLInputElement>['onChange'] = (e) => this.setState({ search: e.target.value });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment