Комментарии 13
Что-то потоки какие-то переусложненные и при этом довольно слабые.
Если сравнивать с rx, то, судя по приведенному коду, видно следующее:
Любой поток работает как BehaviorSubject, т.е. хранит последнее значение. Это вроде бы удобно — но совершенно бесполезно для потоков, которые не представляют какое-то значение (и пример такого потока — как раз useInterval).
Нет оператора switchMap, а ведь это один из наиболее важных операторов rx.js
Смотрите как могла выглядеть функция useInterval с оператором switchMap:
function useInterval(delay$) { return delay$.switchMap(delay => { const res = stream() const id = setInterval(res, delay, delay); res.end.map(() => clearInterval(id)); return res; }) }
Не различаются окончание потока и его отмена.
// ссылка на поток tick
const tick = useInterval({ delay })
// здесь мы создаем дочерний поток для побочных действий
// каждый раз когда запускается новый интервал мы увеличиваем счетчик
tick.map(() => count( count() + 1 ))
// после каждого увеличения счетчика вызываем отрисовку
count.map(m.redraw)
2. switchMap реализован идеоматичным для потоков образом в контексте
theirDelay()
3. parent.end касается только потомков, сам поток родитель parent() остается активным
собственно end и это и есть отмена дочернего потока.
Что бы закрыть контекст дочернего потока, можно использовать stream.SKIP
parent.map( ()=> stream.SKIP )
useInterval — это контекст для вычисления потока count()
И что из этого следует? Кстати, count тоже реализован неоптимально, специально же для таких случаев придуман scan:
const count = Stream.scan(acc => acc+1, 0, tick);
switchMap реализован идеоматичным для потоков образом в контексте theirDelay()
Не вижу ничего похожего на switchMap.
parent.end касается только потомков, сам поток родитель parent() остается активным собственно end и это и есть отмена дочернего потока.
Во-первых, нет, не остаётся. Если сделать parent.end(true)
— то поток parent
перейдёт в состояние "ended"
, в котором он игнорирует любые попытки обновления.
Во-вторых, как эти рассуждения отменяют невозможность различить что произошло с потоком, был ли он отменён снаружи или "иссяк" изнутри?
Что бы закрыть контекст дочернего потока, можно использовать stream.SKIP parent.map( ()=> stream.SKIP )
Нет, stream.SKIP
не закрывает никакого контекста, это просто пропуск обновления.
2. автор оригинала не хотел использовать scan, я так думаю.
3. theirDelay это не switchMap, это theirDelay, который можно реализовать даже не трогая контекст useInterval.
4. A stream can stop affecting its dependent streams by calling stream.end(true). This effectively removes the connection between a stream and its dependent streams.
mithril.js.org/stream.html#ended-state
5. нам не нужно вычислять, мы точно знаем, что хотим закрыть поток
6. A special value that can be returned to stream callbacks to skip execution of downstreams
You can prevent dependent streams from being updated by returning the special value stream.SKIP
var skipped = stream.combine(function(stream) {
return stream.SKIP
}, [stream(1)])
skipped.map(function() {
// never runs
})
контекст нужен, для того, что бы вычислять в контексте.
Я всё ещё не понимаю что вы пытаетесь сказать.
theirDelay это не switchMap, это theirDelay, который можно реализовать даже не трогая контекст useInterval.
Ну так где реализация switchMap-то? Вы писали, что он где-то там идеоматичным образом реализован.
A stream can stop affecting its dependent streams by calling stream.end(true). This effectively removes the connection between a stream and its dependent streams.
И что из этого следует?
нам не нужно вычислять, мы точно знаем, что хотим закрыть поток
А если нужно?
A special value that can be returned to stream callbacks to skip execution of downstreams
И?
knockout намного умнее этой библиотеки, он, к примеру, вот в этой ситуации без труда устраняет как лишний пересчёт потока c
, так и лишний вызов console.log
:
ko.options.deferUpdates = true;
const x = ko.observable(1)
const y = ko.observable(2)
const z = ko.observable(3)
const a = ko.pureComputed(() => x() - y())
const b = ko.pureComputed(() => y() + z())
const c = ko.pureComputed(() => a() + b())
ko.computed(() => console.log(c()))
y(6);
А ещё knockout не требует явно указывать зависимости.
Ловушки и потоки