##// END OF EJS Templates
Code cleanup,...
cin -
r22:93dca6f27f52 propose observables
parent child
Show More
@@ -0,0 +1,55
1 # Observable
2
3 Универсальный способ организации потока сообщений. Данный механизм может
4 использоваться для оповещения об изменениях состояний объектов или для доставки
5 самостоятельных событий, например, связанных с действиями пользователя.
6
7 Является реализацией классического наблюдателя с возможность сообщить о коце
8 потока событий. Данная реализация не содержит никаких дополнительных функций,
9 таких как фильтрация, канал с состоянием, преобразования сообщений и т.п. Это
10 сделано специально, чтобы реализация оставалась максимально простой.
11
12 Пример того, как можно создать последовательность из 10 событий:
13
14 ```ts
15
16 var events = new Observable(async (notify, error, complete) => {
17 // цикл в котором возникает событие
18 for(let i = 0; i < 10; i++) {
19 await delay(1000);
20 // в качестве данных передается номер события
21 notify(i);
22 }
23 // по окончании последовательности информируем, что событий больше не будет
24 compelte();
25 });
26
27 // создаем окно с отображением хода событий
28 var progress = showProgress({ min: 0, max: 9, current: 0});
29
30 // подписываемся на события
31 events.on(
32 // обработчик очередного события
33 msg => {
34 progress.setValue(msg);
35 }.
36 // обработчик ошибки
37 e => {
38 progress.showError(e);
39 },
40 // обработчик конца потока
41 () => {
42 progress.close();
43 }
44 );
45
46 ```
47
48 ```ts
49
50 // превращаем события dom в Observable
51 var events = new Observable((notify) => {
52 on(domNode,'mousemove', notify);
53 });
54
55 ``` No newline at end of file
@@ -1,6 +1,8
1 define(["./TraceSource"], function (TraceSource) {
1 define(["./TraceSource"], function (TraceSource_1) {
2 2 'use strict';
3 3
4 var TraceSource = TraceSource_1.TraceSource;
5
4 6 return {
5 7
6 8 on: function (filter, cb) {
@@ -1,13 +1,13
1 1 import { IActivationController, IActivatable, ICancellation } from '../interfaces';
2 2 import { AsyncComponent } from './AsyncComponent';
3 3 import { Cancellation } from '../Cancellation';
4 import * as TraceSource from '../log/TraceSource';
4 import { TraceSource } from '../log/TraceSource';
5 5
6 6 type Constructor<T = {}> = new (...args: any[]) => T;
7 7
8 8 const log = TraceSource.get('@implab/core/components/ActivatableMixin');
9 9
10 function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
10 export function ActivatableMixin<TBase extends Constructor<AsyncComponent>>(Base: TBase) {
11 11 return class extends Base implements IActivatable {
12 12 _controller: IActivationController;
13 13
@@ -80,8 +80,4 function ActivatableMixin<TBase extends
80 80 }
81 81 }
82 82
83 namespace ActivatableMixin {
84 export const traceSource = log;
85 }
86
87 export = ActivatableMixin; No newline at end of file
83 export const traceSource = log; No newline at end of file
@@ -1,17 +1,40
1 1 import { Cancellation } from "../Cancellation";
2 import { IAsyncComponent, ICancellation } from "../interfaces";
2 import { IAsyncComponent, ICancellation, ICancellable, IDestroyable } from "../interfaces";
3 import { destroy } from "../safe";
3 4
4 export class AsyncComponent implements IAsyncComponent {
5 export class AsyncComponent implements IAsyncComponent, ICancellable {
6 _cancel: (e) => void;
7
5 8 _completion: Promise<void> = Promise.resolve();
6 9
7 10 getCompletion() { return this._completion };
8 11
9 12 runOperation(op: (ct: ICancellation) => any, ct: ICancellation = Cancellation.none) {
13 // create inner cancellation bound to the passed cancellation token
14 let h: IDestroyable;
15 let inner = new Cancellation(cancel => {
16
17 this._cancel = cancel;
18 h = ct.register(cancel);
19 });
20
10 21 // TODO create cancellation source here
11 async function guard() {
12 await op(ct);
22 let guard = async () => {
23 try {
24 await op(inner);
25 } finally {
26 // after the operation is complete we need to cleanup the
27 // resources
28 destroy(h);
29 this._cancel = null;
30 }
13 31 }
14 32
15 33 return this._completion = guard();
16 34 }
35
36 cancel(reason) {
37 if (this._cancel)
38 this._cancel(reason);
39 }
17 40 } No newline at end of file
@@ -22,7 +22,7 interface IObserver<T> {
22 22
23 23 const noop = () => {};
24 24
25 class Observable<T> implements IObservable<T> {
25 export class Observable<T> implements IObservable<T> {
26 26 private _once = new Array<IObserver<T>>();
27 27
28 28 private _observers = new Array<IObserver<T>>();
@@ -190,9 +190,4 class Observable<T> implements IObservab
190 190 this._notify(guard);
191 191 this._observers = [];
192 192 }
193 }
194
195 namespace Observable {
196 }
197
198 export = Observable; No newline at end of file
193 } No newline at end of file
@@ -1,8 +1,31
1 1 import * as format from '../text/format'
2 2 import { argumentNotNull } from '../safe';
3 import * as Observable from '../components/Observable'
3 import { Observable } from '../components/Observable'
4 4 import { IDestroyable } from '../interfaces';
5 import * as TraceEvent from './TraceEvent'
5
6 export const DebugLevel = 400;
7
8 export const LogLevel = 300;
9
10 export const WarnLevel = 200;
11
12 export const ErrorLevel = 100;
13
14 export const SilentLevel = 0;
15
16 export class TraceEvent {
17 readonly source: TraceSource;
18
19 readonly level: Number;
20
21 readonly arg: any;
22
23 constructor(source: TraceSource, level: Number, arg: any) {
24 this.source = source;
25 this.level = level;
26 this.arg = arg;
27 }
28 }
6 29
7 30 class Registry {
8 31 static readonly instance = new Registry();
@@ -56,14 +79,21 class Registry {
56 79 }
57 80 }
58 81
59 class TraceSource extends Observable<TraceEvent> {
82 export class TraceSource {
60 83 readonly id: any
61 84
62 85 level: number
63 86
87 readonly events: Observable<TraceEvent>
88
89 _notifyNext: (arg: TraceEvent) => void
90
64 91 constructor(id: any) {
65 super();
92
66 93 this.id = id || new Object();
94 this.events = new Observable((next) => {
95 this._notifyNext = next;
96 })
67 97 }
68 98
69 99 protected emit(level: number, arg: any) {
@@ -71,37 +101,37 class TraceSource extends Observable<Tra
71 101 }
72 102
73 103 isDebugEnabled() {
74 return this.level >= TraceSource.DebugLevel;
104 return this.level >= DebugLevel;
75 105 }
76 106
77 107 debug(msg: string, ...args: any[]) {
78 if (this.isEnabled(TraceSource.DebugLevel))
79 this.emit(TraceSource.DebugLevel, format(msg, args));
108 if (this.isEnabled(DebugLevel))
109 this.emit(DebugLevel, format(msg, args));
80 110 }
81 111
82 112 isLogEnabled() {
83 return this.level >= TraceSource.LogLevel;
113 return this.level >= LogLevel;
84 114 }
85 115
86 116 log(msg: string, ...args: any[]) {
87 if (this.isEnabled(TraceSource.LogLevel))
88 this.emit(TraceSource.LogLevel, format(msg, args));
117 if (this.isEnabled(LogLevel))
118 this.emit(LogLevel, format(msg, args));
89 119 }
90 120
91 121 isWarnEnabled() {
92 return this.level >= TraceSource.WarnLevel;
122 return this.level >= WarnLevel;
93 123 }
94 124
95 125 warn(msg: string, ...args: any[]) {
96 if (this.isEnabled(TraceSource.WarnLevel))
97 this.emit(TraceSource.WarnLevel, format(msg, args));
126 if (this.isEnabled(WarnLevel))
127 this.emit(WarnLevel, format(msg, args));
98 128 }
99 129
100 130 /**
101 131 * returns true if errors will be recorded.
102 132 */
103 133 isErrorEnabled() {
104 return this.level >= TraceSource.ErrorLevel;
134 return this.level >= ErrorLevel;
105 135 }
106 136
107 137 /**
@@ -111,8 +141,8 class TraceSource extends Observable<Tra
111 141 * @param args parameters which will be substituted in the message.
112 142 */
113 143 error(msg: string, ...args: any[]) {
114 if (this.isEnabled(TraceSource.ErrorLevel))
115 this.emit(TraceSource.ErrorLevel, format(msg, args));
144 if (this.isEnabled(ErrorLevel))
145 this.emit(ErrorLevel, format(msg, args));
116 146 }
117 147
118 148 /**
@@ -156,16 +186,3 class TraceSource extends Observable<Tra
156 186 }
157 187 }
158 188
159 namespace TraceSource {
160 export const DebugLevel = 400;
161
162 export const LogLevel = 300;
163
164 export const WarnLevel = 200;
165
166 export const ErrorLevel = 100;
167
168 export const SilentLevel = 0;
169 }
170
171 export = TraceSource; No newline at end of file
@@ -1,9 +1,8
1 1 import { IObservable, IDestroyable, ICancellation } from "../../interfaces";
2 import * as TraceEvent from '../TraceEvent';
3 2 import { Cancellation } from "../../Cancellation";
4 import * as TraceSource from "../TraceSource";
3 import { TraceEvent, LogLevel, WarnLevel } from "../TraceSource";
5 4
6 class ConsoleWriter implements IDestroyable {
5 export class ConsoleWriter implements IDestroyable {
7 6 readonly _subscriptions = new Array<IDestroyable>();
8 7
9 8 writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) {
@@ -15,9 +14,9 class ConsoleWriter implements IDestroya
15 14 }
16 15
17 16 writeEvent(next: TraceEvent) {
18 if (next.level >= TraceSource.LogLevel) {
17 if (next.level >= LogLevel) {
19 18 console.log(next.source.id.toString(), next.arg);
20 } else if(next.level >= TraceSource.WarnLevel) {
19 } else if(next.level >= WarnLevel) {
21 20 console.warn(next.source.id.toString(), next.arg);
22 21 } else {
23 22 console.error(next.source.id.toString(), next.arg);
@@ -27,9 +26,4 class ConsoleWriter implements IDestroya
27 26 destroy() {
28 27 this._subscriptions.forEach(x => x.destroy());
29 28 }
30 }
31
32 namespace ConsoleWriter {
33 }
34
35 export = ConsoleWriter; No newline at end of file
29 } No newline at end of file
@@ -228,4 +228,9 export function first(sequence: any, cb:
228 228 return err(new Error("The sequence is required"));
229 229 else
230 230 throw new Error("The sequence is required");
231 }
232
233 export function destroy(d: any) {
234 if (d && 'destroy' in d)
235 d.destroy();
231 236 } No newline at end of file
@@ -3,7 +3,8 define(["tape"], function(tape) {
3 3 var sourceId = '73a633f3-eab8-49b0-8601-07cae710f234';
4 4 var sourceId2 = '3ba9c7cd-ed77-437b-9a2f-1cbeb1226b5b';
5 5 tape('Load TraceSource for the module', function(t) {
6 require(["core/log/trace!" + sourceId, "core/log/TraceSource"], function(trace, TraceSource) {
6 require(["core/log/trace!" + sourceId, "core/log/TraceSource"], function(trace, TraceSource_1) {
7 var TraceSource = TraceSource_1.TraceSource;
7 8 t.equal(trace && trace.id, sourceId, "trace should be taken from the loader plugin parameter");
8 9
9 10 var count = 0;
@@ -1,5 +1,5
1 1 import * as tape from 'tape';
2 import * as ActivatableMixin from '@implab/core/components/ActivatableMixin';
2 import { ActivatableMixin} from '@implab/core/components/ActivatableMixin';
3 3 import { AsyncComponent } from '@implab/core/components/AsyncComponent';
4 4 import { IActivationController, IActivatable, ICancellation } from '@implab/core/interfaces';
5 5 import { Cancellation } from '@implab/core/Cancellation';
@@ -1,9 +1,8
1 import { IObservable, ICancellation, IDestroyable } from "../../build/dist/interfaces";
2 import * as TraceEvent from '../../build/dist/log/TraceEvent';
3 import { Cancellation } from "../../build/dist/Cancellation";
4 import * as TraceSource from "../../build/dist/log/TraceSource";
1 import { IObservable, ICancellation, IDestroyable } from "@implab/core/interfaces";
2 import { Cancellation } from "@implab/core/Cancellation";
3 import { TraceEvent, LogLevel, WarnLevel } from "@implab/core/log/TraceSource";
5 4 import * as tape from 'tape';
6 import { argumentNotNull } from "../../build/dist/safe";
5 import { argumentNotNull } from "@implab/core/safe";
7 6
8 7 export class TapeWriter implements IDestroyable {
9 8 readonly _tape: tape.Test
@@ -24,9 +23,9 export class TapeWriter implements IDest
24 23 }
25 24
26 25 writeEvent(next: TraceEvent) {
27 if (next.level >= TraceSource.LogLevel) {
26 if (next.level >= LogLevel) {
28 27 this._tape.comment("LOG " + next.arg);
29 } else if (next.level >= TraceSource.WarnLevel) {
28 } else if (next.level >= WarnLevel) {
30 29 this._tape.comment("WARN " + next.arg);
31 30 } else {
32 31 this._tape.comment("ERROR " + next.arg);
@@ -1,4 +1,4
1 import * as TraceSource from '@implab/core/log/TraceSource'
1 import { TraceSource, DebugLevel } from '@implab/core/log/TraceSource'
2 2 import * as tape from 'tape';
3 3 import { TapeWriter } from './TestTraits';
4 4
@@ -7,11 +7,11 const sourceId = 'test/TraceSourceTests'
7 7 tape('trace message', t => {
8 8 let trace = TraceSource.get(sourceId);
9 9
10 trace.level = TraceSource.DebugLevel;
10 trace.level = DebugLevel;
11 11
12 let h = trace.on((ev) => {
12 let h = trace.events.on((ev) => {
13 13 t.equal(ev.source, trace, "sender should be the current trace source");
14 t.equal(ev.level, TraceSource.DebugLevel, "level should be debug level");
14 t.equal(ev.level, DebugLevel, "level should be debug level");
15 15 t.equal(ev.arg, "Hello, World!", "The message should be a formatted message");
16 16
17 17 t.end();
@@ -25,21 +25,21 tape('trace message', t => {
25 25 tape('trace event', t => {
26 26 let trace = TraceSource.get(sourceId);
27 27
28 trace.level = TraceSource.DebugLevel;
28 trace.level = DebugLevel;
29 29
30 30 let event = {
31 31 name: "custom event"
32 32 };
33 33
34 let h = trace.on((ev) => {
34 let h = trace.events.on((ev) => {
35 35 t.equal(ev.source, trace, "sender should be the current trace source");
36 t.equal(ev.level, TraceSource.DebugLevel, "level should be debug level");
36 t.equal(ev.level, DebugLevel, "level should be debug level");
37 37 t.equal(ev.arg, event, "The message should be the specified object");
38 38
39 39 t.end();
40 40 });
41 41
42 trace.traceEvent(TraceSource.DebugLevel, event);
42 trace.traceEvent(DebugLevel, event);
43 43
44 44 h.destroy();
45 45 });
@@ -48,11 +48,11 tape('tape comment writer', async t => {
48 48 let writer = new TapeWriter(t);
49 49
50 50 TraceSource.on(ts => {
51 writer.writeEvents(ts);
51 writer.writeEvents(ts.events);
52 52 });
53 53
54 54 let trace = TraceSource.get(sourceId);
55 trace.level = TraceSource.DebugLevel;
55 trace.level = DebugLevel;
56 56
57 57 trace.log("Hello, {0}!", 'World');
58 58 trace.log("Multi\n line");
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now