# Реактивные запросы — `$.watch`

`watch` отвечает на **вхождение в выборку**: узел попал под селектор или
перестал под него подходить. Это не то же, что `<trigger>` и `$.triggers`
([triggers.md](highlevel/triggers)): те срабатывают на **пересечение в мире**, а watch —
на изменение состава выборки (удаление, смена класса, выход из `within()`).

```js
const w = $.watch('.enemy:dead', {
    onEnter: (node) => $.sound.play('die.ogg'),
    onLeave: (node) => $.log('враг ожил?'),
});
w.stop();
```

---

## 1. Методы

| Вызов | Смысл |
|---|---|
| `$.watch(sel, handlers)` | подписаться; `handlers` — функция (= `onEnter`) или `{ onEnter, onLeave, immediate }` |
| `handle.stop()` | прекратить наблюдение (повторно — безопасно) |
| `handle.size()` | сколько узлов в выборке по последнему тику |
| `handle.active()` / `handle.selector()` | живо ли наблюдение / его селектор |
| `$.watch.count()` / `.clear()` / `.list()` | сколько наблюдений, снять все, перечислить (`{ id, sel, size }`) |

## 2. Когда срабатывает вход

* по умолчанию вход **не** срабатывает для узлов, которые уже подходили под
  селектор в момент подписки: «вошёл» значит «вошёл потом». Нужны они тоже —
  `{ immediate: true }`;
* вход для узла, который был удалён и создан заново, сработает: сравнение идёт
  по `uid`, а не по id;
* выход отдаёт **последнюю известную ссылку** на узел: он может быть уже
  удалён, поэтому `.attr('id')` читается, а физика/отрисовка — нет;
* шаг выполняется раз в кадр (`tickWatch()` в цикле `$`), после триггеров.

## 3. Цена

Кадр без наблюдений не стоит ничего: `tickWatch()` выходит на первой проверке.
С каждым наблюдением — один запрос `query(sel)` в кадр: для структурных
селекторов это готовая выборка из индекса реестра
([core.md](highlevel/core), `docs/highlevel/_CONTRACT.md` §2), для условий вроде
`[hp<20]` — проход по якорю. Наблюдений должно быть немного (единицы), иначе
дешевле переписать на события узла.

## 4. Проверка

```bash
build/_deps/quickjs-build/qjs tests/js/watch_test.mjs     # без движка
python3 tests/agent/watch_test.py                          # в движке
```
