##// END OF EJS Templates
the documentation on observables is added...
cin -
r26:0b0a30e050ba propose observables
parent child
Show More
@@ -31,7 +31,7 events.on(
31 31 // обработчик очередного события
32 32 msg => {
33 33 progress.setValue(msg);
34 },
34 }.
35 35 // обработчик ошибки
36 36 e => {
37 37 progress.showError(e);
@@ -41,40 +41,55 events.on(
41 41 progress.close();
42 42 }
43 43 );
44
45 // ожидание следующего события
46 let firstEvent = await events.next();
44 47 ```
45 48
46 Пример создания `Observable` из событий другого объекта, например, виджета:
49 `Observable` можно создавать из событий другого объекта, например, виджета:
47 50
48 51 ```ts
52 // клсс
53 class Canvas {
54 readonly mouseMove: IObservable<[number,number]>
55
49 56 postCreate() {
50 57 // превращаем события виджета в Observable
51 this.mouseMove = new Observable((notify) => {
52 this.moveArea.on('mousemove',(x) => notify(x.) );
58 this.mouseMove = new Observable<[number,number]>((notify) => {
59 this.mousePad.on('mousemove',(e) => notify([e.clientX, e.clientY]) );
53 60 });
54 61 }
62 }
55 63
56 64 ```
57 65
58 Пример инициализации `Observable` внутри класса и генерация событий:
66 Если объект инкапсулирует в себе `Observable`, он также может сохранить методы
67 для оповещения подписчиков для дальнейшего их использования внутри класса.
59 68
60 69 ```ts
61
62 class PositionWidget extends Widget {
70 // класс, который будет генерировать события местоположения
71 class PositionTracker implements IDestroyable {
72 // _nextPosition и _complete будут связаны с position при создании
73 // экземпляра PositionTracker.
63 74 _nextPosition: (pos: Position) => void
64
65 75 _complete: () => void
66 76
67 readonly position: Observable<Position>;
77 readonly position: IObservable<Position>
68 78
69 constructor(...args[]) {
79 // конструктор
80 constructor(...args: any[]) {
70 81 super(args);
71 82
83 // создаем Observable
72 84 this.position = new Observable<Position>((notify, error, complete) => {
85 // сохраняем методы для оповещения о новом местоположении
73 86 this._nextPosition = notify;
87 // метод об оповещении конца потока событий
74 88 this._complete = complete
75 89 });
76 90 }
77 91
92 // метод для очистки ресурсов
78 93 destroy() {
79 94 this._complete();
80 95
@@ -82,4 +97,39 class PositionWidget extends Widget {
82 97 }
83 98 }
84 99
100 ```
101
102 ## Observable и последовательности
103
104 Можно сичтать, что `Observable` это некоторая аналогия итератора только в
105 парадигме событийного (или реактивного) программировния. Следует также понимать,
106 что при переходе от синхронного процедурного программирования к событийному так
107 же меняется и направление управления (Inverse Of Control), что означает
108 следующее:
109
110 * при работе с итераторами клиенты сами определяют момент чтения следующего
111 элемента последовательности.
112 * при работе с `Observable` клиенты вынуждены обрабатывать эти события по мере
113 их поступления и не могут на это повлиять.
114
115 Последний пункт можно изменить применив, например, буффер или канал с
116 состоянием, т.е. очередь, но данные механизмы выходят за рамки простого шаблона
117 наблюдателя.
118
119 ```ts
120 while(1) {
121 // ожидаем следующее событие, по сути это подписка только на одно событие
122 let next = await events.next();
123
124 // такой цикл может пропускать сообщения, поскольку асинхронная операция
125 // позволит возобновить создание новых событий, на которые мы не подписаны
126 await processEvent(next);
127
128 // не только асинхронные операции могут привести к пропуску события
129 // например вызов метода, который приводит к созданию события так же
130 // приведет к тому, что созданное событие не будет обработано в текущем
131 // цикле
132 doSmthAndRiseEvent();
133 }
134
85 135 ``` No newline at end of file
@@ -31,7 +31,7 events.on(
31 31 // обработчик очередного события
32 32 msg => {
33 33 progress.setValue(msg);
34 },
34 }.
35 35 // обработчик ошибки
36 36 e => {
37 37 progress.showError(e);
@@ -41,45 +41,146 events.on(
41 41 progress.close();
42 42 }
43 43 );
44
45 // ожидание следующего события
46 let firstEvent = await events.next();
44 47 ```
45 48
46 Пример создания `Observable` из событий другого объекта, например, виджета:
49 `Observable` можно создавать из событий другого объекта, например, виджета:
47 50
48 51 ```ts
52 // клсс
53 class Canvas {
54 readonly mouseMove: IObservable<[number,number]>
55
49 56 postCreate() {
50 57 // превращаем события виджета в Observable
51 this.mouseMove = new Observable((notify) => {
52 this.moveArea.on('mousemove',(x) => notify(x.) );
58 this.mouseMove = new Observable<[number,number]>((notify) => {
59 this.mousePad.on('mousemove',(e) => notify([e.clientX, e.clientY]) );
53 60 });
54 61 }
62 }
55 63
56 64 ```
57 65
58 Пример инициализации `Observable` внутри класса и генерация событий:
66 Если объект инкапсулирует в себе `Observable`, он также может сохранить методы
67 для оповещения подписчиков для дальнейшего их использования внутри класса.
59 68
60 69 ```ts
61
62 class PositionWidget extends Widget {
70 // класс, который будет генерировать события местоположения
71 class PositionTracker implements IDestroyable {
72 // _nextPosition и _complete будут связаны с position при создании
73 // экземпляра PositionTracker.
63 74 _nextPosition: (pos: Position) => void
64
65 75 _complete: () => void
66 76
67 readonly position: Observable<Position>;
77 readonly position: IObservable<Position>
68 78
69 constructor(...args[]) {
79 // конструктор
80 constructor(...args: any[]) {
70 81 super(args);
71 82
83 // создаем Observable
72 84 this.position = new Observable<Position>((notify, error, complete) => {
85 // сохраняем методы для оповещения о новом местоположении
73 86 this._nextPosition = notify;
87 // метод об оповещении конца потока событий
74 88 this._complete = complete
75 89 });
76 90 }
77 91
92 // метод для очистки ресурсов
78 93 destroy() {
79 94 this._complete();
80 95
81 96 super();
82 97 }
83 98 }
99 ```
84 100
101 Существует также несколько варинатов получения сообщений
102
103 ```ts
104 // регистрация метода для получений событий
105 let subscription = pushEvents.on((msg) => {
106 displayPopup(msg);
107 });
108
109 // подписку можно отменить, после чего обработчики больше не будут вызываться
110 subcription.destroy();
111
112 // если требуется получить только одно сообщение можно использовать
113 // асинхронный метод next(ct?: ICancellation)
114
115 let msg = await pushEvents.next();
116
117 // пример метода для получения координат с карты, который использует
118 // событие нажатия мышью для определения координат.
119
120 class Map {
121 /**
122
123 Получает координаты по щелчку мыши.
124
125 @async
126
127 @returns [lon,lat]
128
129 */
130 async peekCoordinates(ct: ICancellation = Cancellation.none) {
131 // получаем событие клика
132 let evt = this.viewport.click.next(ct);
133
134 // преобразуем позицию на экране в координаты карты
135 return this.clientToCoodinates([evt.clientx,evt.clientY]);
136 }
137 }
138
139
140 let map : Map; // где-то объявлено
141
142 // пример получения координат с карты
143 let coords = await map.peekCoordinates();
144
145 ```
146
147 ## Observable и последовательности
148
149 Можно сичтать, что `Observable` это некоторая аналогия итератора только в
150 парадигме событийного (или реактивного) программировния. Следует также понимать,
151 что при переходе от синхронного процедурного программирования к событийному так
152 же меняется и направление управления (Inverse Of Control), что означает
153 следующее:
154
155 * при работе с итераторами клиенты сами определяют момент чтения следующего
156 элемента последовательности.
157 * при работе с `Observable` клиенты вынуждены обрабатывать эти события по мере
158 их поступления и не могут на это повлиять.
159
160 Последний пункт можно изменить применив, например, буффер или канал с
161 состоянием, т.е. очередь, но данные механизмы выходят за рамки простого шаблона
162 наблюдателя.
163
164 ```ts
165 // обработка в цикле не гарантирует получения всех сообщений
166 while(1) {
167 // ожидаем следующее событие, по сути это подписка только на одно событие
168 let next = await events.next();
169
170 // такой цикл может пропускать сообщения, поскольку асинхронная операция
171 // позволит возобновить создание новых событий, на которые мы не подписаны
172 await processEvent(next);
173
174 // не только асинхронные операции могут привести к пропуску события
175 // например вызов метода, который приводит к созданию события так же
176 // приведет к тому, что созданное событие не будет обработано в текущем
177 // цикле
178 doSmthAndRiseEvent();
179 }
180
181 // для получения всех сообщений нужно регистрировать подписчика
182 events.on((data) => {
183 // будет вызван для всех сообщений
184 processEvent(data);
185 });
85 186 ``` No newline at end of file
@@ -176,6 +176,7 export class Observable<T> implements IO
176 176
177 177 this._notify(guard);
178 178 this._observers = [];
179 this._complete = true;
179 180 }
180 181
181 182 protected _notifyCompleted() {
@@ -189,5 +190,6 export class Observable<T> implements IO
189 190
190 191 this._notify(guard);
191 192 this._observers = [];
193 this._complete = true;
192 194 }
193 195 } No newline at end of file
@@ -17,20 +17,58 tape('events sequence example', async t
17 17 await delay(0);
18 18 notify(i);
19 19 }
20 complete();
20 21 resolve();
21 22 });
22 23 });
23 24
24 25 let count = 0;
25 events.on(x => count = count + x);
26 let complete = false;
27 events.on(x => count = count + x, null, () => complete = true);
26 28
27 29 let first = await events.next();
28 30
29 31 t.equals(first, 0, "the first event");
32 t.false(complete, "the sequence is not complete");
30 33
31 34 await done;
32 35
33 36 t.equals(count, 45, "the summ of the evetns");
37 t.true(complete, "the sequence is complete");
38
39 t.end();
40 });
41
42 tape('event sequence termination', async t => {
43 let events: IObservable<number>
44
45 let done = new Promise<void>((resolve) => {
46 events = new Observable<number>(async (notify, fail, complete) => {
47 await delay(0);
48 notify(1);
49 complete();
50 notify(2);
51 complete();
52 fail("Sequence terminated");
53 resolve();
54 });
55 });
56
57 let count = 0;
58 events.on(() => {}, (e) => count++, () => count++);
59
60 let first = await events.next();
61 t.equals(first, 1, "the first message");
62 try {
63 await events.next();
64 t.fail("shoud throw an exception");
65 } catch(e) {
66 t.pass("the sequence is terminated");
67 }
68
69 await done;
70
71 t.equals(count, 1, "the sequence must be terminated once");
34 72
35 73 t.end();
36 74 }); No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now