Недавно я опубликовал статью под названием "Почему я люблю RxJS: кольцевой буфер в 4 строках кода". Вскоре после этого мне пришлось отредактировать его, потому что я нашел решение, которое было еще короче (🥳), а также потому, что исходная реализация содержала довольно серьезный недостаток, который я не хотел, чтобы люди копировали (😭).



Позвольте мне рассказать вам, что это было, чтобы вы могли предотвратить это в своих собственных операторах RxJS.

Реальный пример

Надеюсь, вы знакомы с концепцией кольцевого/кольцевого буфера. Если нет, вы также можете ознакомиться с моей другой статьей. Вкратце, это:

Далее вы увидите оригинальную и обновленную реализацию кольцевого буфера из моей вышеупомянутой статьи.

Чем новое решение лучше старого? Легкий. Это более лаконично и читабельно, но обеспечивает ту же функциональность.

Тот же функционал?

Ну не совсем! И есть загвоздка.

⚠️ Следите за тем, где вы объявляете состояние ⚠️

Если бы вы дважды подписались на Observable, который использует исходную реализацию, произошло бы следующее:

Посмотрите еще раз, где в операторе объявляется состояние — buffer. Поскольку bufferRing(4) выполняется только один раз, обе подписки теперь используют один и тот же «буфер», что приводит к более или менее неопределенному поведению.

Итак, где объявить состояние?

👨‍🏫 Резюме

оператор RxJS — это функция, которая принимает исходный наблюдаемый объект и возвращает новый наблюдаемый объект. Если мы хотим передать оператору аргументы, например size в нашем случае, нам нужно подняться на один уровень абстракции выше и написать функцию, которая возвращает функцию, которая возвращает Observable.

Если логика вашего оператора настолько сложна, что вы решили также использовать переменные, правильное место для их объявления — внутри возвращаемого Observable, точнее, в его subscribe-функции. Это будет выполняться каждый раз, когда оператор подписывается. Например, уродливый кольцевой буфер из предыдущего примера можно было бы переписать так:

… делая его еще уродливее 😬

Другой вариант - использовать defer:

Это избавляет вас от необходимости писать логику демонтажа (в данном случае).

И, наконец, вы также можете попытаться полностью обойти объявление переменных, повторно используя существующие операторы, как я сделал в новом решении с кольцевым буфером. Если вы работаете с одним оператором, это прекрасно. Если вам нужно больше, вы можете объединить их, используя pipe (что не то же самое, что Observable.pipe()).

Оглядываясь назад на мое первоначальное решение, pipe также было полностью избыточным. Как обидно 🙃

В любом случае, надеюсь, вы чему-то научились. До следующего раза 🍻