| @@ -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 | progress.close(); |
|
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 | ```ts |
|
48 | ```ts | |
| 52 | // клсс |
|
49 | postCreate() { | |
| 53 | class Canvas { |
|
50 | // превращаем события виджета в Observable | |
| 54 | readonly mouseMove: IObservable<[number,number]> |
|
51 | this.mouseMove = new Observable((notify) => { | |
| 55 |
|
52 | this.moveArea.on('mousemove',(x) => notify(x.) ); | ||
| 56 | postCreate() { |
|
53 | }); | |
| 57 | // превращаем события виджета в Observable |
|
|||
| 58 | this.mouseMove = new Observable<[number,number]>((notify) => { |
|
|||
| 59 | this.mousePad.on('mousemove',(e) => notify([e.clientX, e.clientY]) ); |
|
|||
| 60 | }); |
|
|||
| 61 | } |
|
|||
| 62 | } |
|
54 | } | |
| 63 |
|
55 | |||
| 64 | ``` |
|
56 | ``` | |
| 65 |
|
57 | |||
| 66 | Если объект инкапсулирует в себе `Observable`, он также может сохранить методы |
|
58 | Пример инициализации `Observable` внутри класса и генерация событий: | |
| 67 | для оповещения подписчиков для дальнейшего их использования внутри класса. |
|
|||
| 68 |
|
59 | |||
| 69 | ```ts |
|
60 | ```ts | |
| 70 | // класс, который будет генерировать события местоположения |
|
61 | ||
| 71 | class PositionTracker implements IDestroyable { |
|
62 | class PositionWidget extends Widget { | |
| 72 | // _nextPosition и _complete будут связаны с position при создании |
|
|||
| 73 | // экземпляра PositionTracker. |
|
|||
| 74 |
|
|
63 | _nextPosition: (pos: Position) => void | |
|
|
64 | ||||
| 75 | _complete: () => void |
|
65 | _complete: () => void | |
| 76 |
|
66 | |||
| 77 |
readonly position: |
|
67 | readonly position: Observable<Position>; | |
| 78 |
|
68 | |||
| 79 | // конструктор |
|
69 | constructor(...args[]) { | |
| 80 | constructor(...args: any[]) { |
|
|||
| 81 | super(args); |
|
70 | super(args); | |
| 82 |
|
71 | |||
| 83 | // создаем Observable |
|
|||
| 84 |
|
|
72 | this.position = new Observable<Position>((notify, error, complete) => { | |
| 85 | // сохраняем методы для оповещения о новом местоположении |
|
|||
| 86 |
|
|
73 | this._nextPosition = notify; | |
| 87 | // метод об оповещении конца потока событий |
|
|||
| 88 |
|
|
74 | this._complete = complete | |
| 89 | }); |
|
75 | }); | |
| 90 | } |
|
76 | } | |
| 91 |
|
77 | |||
| 92 | // метод для очистки ресурсов |
|
|||
| 93 |
|
|
78 | destroy() { | |
| 94 | this._complete(); |
|
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 | ``` No newline at end of file |
|
85 | ``` | |
| @@ -1,2 +1,3 | |||||
| 1 |
|
|
1 | //define(["./ActivatableTests", "./trace-test", "./TraceSourceTests", "./CancellationTests"]); | |
| 2 | //define(["./CancellationTests"]); No newline at end of file |
|
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
