Сегодня мы пообщаемся на тему WeakRef + FinalizationRegistry. Эти API находятся на Stage 3 (Candidate) в TC39 и имеют все шансы выйти в свет в ближайшем будущем.
Слабые ссылки, они же WeakRef, просты, как песня, и многим хорошо знакомы по другим runtime.
FinalizationRegistry немного сложнее и, как следует из названия, реализует механизм финализации объектов. Простейший пример его выглядит так.
Эти 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, пишите. Думаю, это будет очень полезно для сообщества.