| @@ -0,0 +1,36 | |||
|
|
1 | import { TraceSource, DebugLevel } from '@implab/core/log/TraceSource' | |
|
|
2 | import * as tape from 'tape'; | |
|
|
3 | import { TapeWriter, delay } from './TestTraits'; | |
|
|
4 | import { Observable } from '@implab/core/Observable'; | |
|
|
5 | import { IObservable } from '@implab/core/interfaces'; | |
|
|
6 | ||
|
|
7 | let trace = TraceSource.get("ObservableTests"); | |
|
|
8 | ||
|
|
9 | tape('events sequence example', async t => { | |
|
|
10 | ||
|
|
11 | ||
|
|
12 | let events: IObservable<number> | |
|
|
13 | ||
|
|
14 | let done = new Promise<void>((resolve) => { | |
|
|
15 | events = new Observable<number>(async (notify, fail, complete) => { | |
|
|
16 | for (let i = 0; i < 10; i++) { | |
|
|
17 | await delay(0); | |
|
|
18 | notify(i); | |
|
|
19 | } | |
|
|
20 | resolve(); | |
|
|
21 | }); | |
|
|
22 | }); | |
|
|
23 | ||
|
|
24 | let count = 0; | |
|
|
25 | events.on(x => count = count + x); | |
|
|
26 | ||
|
|
27 | let first = await events.next(); | |
|
|
28 | ||
|
|
29 | t.equals(first, 0, "the first event"); | |
|
|
30 | ||
|
|
31 | await done; | |
|
|
32 | ||
|
|
33 | t.equals(count, 45, "the summ of the evetns"); | |
|
|
34 | ||
|
|
35 | t.end(); | |
|
|
36 | }); No newline at end of file | |
| @@ -41,55 +41,40 events.on( | |||
|
|
41 | 41 | progress.close(); |
|
|
42 | 42 | } |
|
|
43 | 43 | ); |
|
|
44 | ||
|
|
45 | // ожидание следующего события | |
|
|
46 | let firstEvent = await events.next(); | |
|
|
47 | 44 | ``` |
|
|
48 | 45 | |
|
|
49 |
`Observable` |
|
|
|
46 | Пример создания `Observable` из событий другого объекта, например, виджета: | |
|
|
50 | 47 | |
|
|
51 | 48 | ```ts |
|
|
52 | // клсс | |
|
|
53 | class Canvas { | |
|
|
54 | readonly mouseMove: IObservable<[number,number]> | |
|
|
55 | ||
|
|
56 | 49 |
|
|
|
57 | 50 |
|
|
|
58 |
|
|
|
|
59 |
|
|
|
|
51 | this.mouseMove = new Observable((notify) => { | |
|
|
52 | this.moveArea.on('mousemove',(x) => notify(x.) ); | |
|
|
60 | 53 |
|
|
|
61 | 54 | } |
|
|
62 | } | |
|
|
63 | 55 | |
|
|
64 | 56 | ``` |
|
|
65 | 57 | |
|
|
66 | Если объект инкапсулирует в себе `Observable`, он также может сохранить методы | |
|
|
67 | для оповещения подписчиков для дальнейшего их использования внутри класса. | |
|
|
58 | Пример инициализации `Observable` внутри класса и генерация событий: | |
|
|
68 | 59 | |
|
|
69 | 60 | ```ts |
|
|
70 | // класс, который будет генерировать события местоположения | |
|
|
71 | class PositionTracker implements IDestroyable { | |
|
|
72 | // _nextPosition и _complete будут связаны с position при создании | |
|
|
73 | // экземпляра PositionTracker. | |
|
|
61 | ||
|
|
62 | class PositionWidget extends Widget { | |
|
|
74 | 63 |
|
|
|
64 | ||
|
|
75 | 65 | _complete: () => void |
|
|
76 | 66 | |
|
|
77 |
readonly position: |
|
|
|
67 | readonly position: Observable<Position>; | |
|
|
78 | 68 | |
|
|
79 | // конструктор | |
|
|
80 | constructor(...args: any[]) { | |
|
|
69 | constructor(...args[]) { | |
|
|
81 | 70 | super(args); |
|
|
82 | 71 | |
|
|
83 | // создаем Observable | |
|
|
84 | 72 |
|
|
|
85 | // сохраняем методы для оповещения о новом местоположении | |
|
|
86 | 73 |
|
|
|
87 | // метод об оповещении конца потока событий | |
|
|
88 | 74 |
|
|
|
89 | 75 | }); |
|
|
90 | 76 | } |
|
|
91 | 77 | |
|
|
92 | // метод для очистки ресурсов | |
|
|
93 | 78 |
|
|
|
94 | 79 | this._complete(); |
|
|
95 | 80 | |
| @@ -97,39 +82,4 class PositionTracker implements IDestro | |||
|
|
97 | 82 | } |
|
|
98 | 83 | } |
|
|
99 | 84 | |
|
|
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 | ||
|
|
135 | 85 | ``` No newline at end of file |
| @@ -1,2 +1,3 | |||
|
|
1 |
|
|
|
|
2 | //define(["./CancellationTests"]); No newline at end of file | |
|
|
1 | //define(["./ActivatableTests", "./trace-test", "./TraceSourceTests", "./CancellationTests"]); | |
|
|
2 | //define(["./CancellationTests"]); | |
|
|
3 | define(["./ObservableTests"]); No newline at end of file | |
General Comments 0
You need to be logged in to leave comments.
Login now
