🔥

Тред (Андрей Печкуров)


Сегодня мы пообщаемся на тему WeakRef + FinalizationRegistry. Эти API находятся на Stage 3 (Candidate) в TC39 и имеют все шансы выйти в свет в ближайшем будущем.

Слабые ссылки, они же WeakRef, просты, как песня, и многим хорошо знакомы по другим runtime.
notion image

FinalizationRegistry немного сложнее и, как следует из названия, реализует механизм финализации объектов. Простейший пример его выглядит так.
notion image

Эти API можно пощупать в Node.js v12+, указав флаг --harmony-weak-refs. Но в core слабые ссылки уже давно используются во многих местах. Например, для хранения ссылок на JS объекты в нативных модулях. github.com/nodejs/node/bl…

Есть и собственный утилитный класс WeakReference, который используется в domain. github.com/nodejs/node/bl…

Про WeakMap и WeakSet, думаю, и говорить не нужно. Они используются в нескольких модулях. Кстати, любопытно то, что WeakRef + Map !== WeakMap. Дело в возможных ссылках между ключами и значениями.

WeakMap построен на механизме ephemerons, позволяющем GC корректно обрабатывать циклы. Его семантика такова: пока не собран ключ, ссылка на значение - strong; когда же ключ собран, ссылка на значение трактуется, как weak.

Думаю, многих интересуют хорошие примеры применений новых API. Примеры использования слабых ссылок в core подсказывают нам, что этот API пригодится вам в случаях, если вы не управляете ЖЦ пользовательских объектов, но хотите иметь доступ к ним, пока они достижимы.

Другой неплохой пример - очистка ресурсов WebAssembly при сборке JS объектов. Он хорошо расписан тут. github.com/tc39/proposal-…

Другие примеры из этого документа я бы назвал хорошими с большой натяжкой. Кэш с WeakRef не дает вам гарантий по времени нахождения в нем значений и я бы предпочел ему LRU/LFU кэш. Другие примеры тоже не идеальные и причина в этом одна.

Дело в том, что оба API, ожидаемо, не дают никаких гарантий по детерменистичности своей работы, поскольку они завязаны на GC. Это проблема характерна подобных API во всех языках, а не только для JS.

Поэтому точно не стоит освобождать системные ресурсы (скажем, fd в Linux) или соединения с БД через финализаторы. Иначе, вы рискуете словить непредсказуемые баги в произвольных условиях. Старый добрый блок finally (условно, конечно) куда лучше подходит для подобных случаев.

Если вы знаете еще какие-то хорошие use case'ы для этих экспериментальных API, пишите. Думаю, это будет очень полезно для сообщества.