В Neovim добавили vim.async для модернизации асинхронной архитектуры

3 мин
В Neovim добавили vim.async для модернизации асинхронной архитектуры

Открытый консольный текстовый редактор Neovim, форк от легендарного Vim, взял от своего родителя самое лучшее, внедрив одновременно собственные новшества. В отличие от специфичного VimScript, редактор использует более быстрый и распространенный язык Lua. Преимуществ у этого решения множество, но сегодня мы поговорим об одном из главных — асинхронности.

Зачем она вообще нужна в редакторе? Все просто: Neovim, как и большинство текстовых редакторов, работает в одном главном потоке. Если пытаться, например, выполнять запутанный поиск по проекту с помощью синхронного кода, весь оболочку редактора зависнет до окончания операции. Чтобы этого избежать, Neovim использует event loop на базе библиотеки Libuv (на которой, кстати, построен Node.js).

До появления модуля vim.async разработчикам приходилось применять подходы, которые могли быть вполне неэффективными, а иногда и вовсе проблематичными. В частности, при использовании vim.loop и vim.uv можно было попросить NeoVim асинхронно прочитать файл, а после завершения передать итог callback-функции.

vim.uv.fs_open("input.txt", "r", 438, function(open_error, fd) if open_error then return print(open_error) end vim.uv.fs_read(fd, 4096, 0, function(read_error, data) if read_error then print(read_error) else print(data) end vim.uv.fs_close(fd) end)
end)

Если продолжать усложнять цепочку таких вызовов, можно попасть в так называемый callback hell, нечитаемую «пирамиду смерти». В этом случае код можно было сделать более читаемым с помощью отдельных функций или оберток.

Корутины тоже помогали решать эту задачу: они позволяли приостанавливать выполнение функции до определенного момента. Благодаря этому асинхронный код можно было писать более линейно, но для этой цели использовались сторонние решения, включая plenary.async, async.nvim и собственные абстракции. Такой решение уменьшал вложенность, тем не менее приводил к фрагментации, так как разные плагины могли использовать разные соглашения: о создании задач, обработке ошибок и так далее. Отсутствие единой модели осложняло взаимодействие и делало эффективное управление жизненным циклом задач более трудоемким.

Создайте веб‑приложение и получите бонусы на его деплой в облако

В новом бесплатном курсе по JavaScript.

Подробнее →

Что изменилось с vim.async

vim.async добавляет в стандартную библиотеку встроенную схема структурированной конкурентности,  выступая заменой собственных корутин-оберток. В контексте новой модели асинхронные подпрограммы выполняются внутри задач, создаваемых с помощью vim.async.run(). Когда задача ожидает события или операции ввода-вывода c использованием vim.async.await(), NeoVim останавливает выполнение фрейма и возвращает управление event loop. Это гарантирует, что синхронные операции редактора и пользовательский ввод продолжатся без прерываний.

Появляется строгая иерархия этих задач, в которой обеспечиваются четкие отношения между родительскими и дочерними элементами. В этой иерархии родительская проблема не разрешится, пока не будут завершены все дочерние. К тому же, необработанные исключения внутри дочерней задачи немедленно передаются родительской, запуская отмену во всех родственных задачах, если они не были изолированы. Есть и исключения: если разработчику необходимо, чтобы асинхронный процесс все равно продолжал работу после завершения инициирующей задачи, то вызов task:detach() переведет его работу в статус независимого процесса высокого уровня.

Сейчас представлены следующие примитивы, с помощью которых можно управлять асинхронностью:

  • vim.async.semaphore() — ограничивает количество одновременных операций. Это может быть полезным, в частности, при обработке большого количества файлов или сетевых запросов.

  • vim.async.timeout() — задает временное ограничение для операции или групп операций, отменяя их выполнение при превышении заданного времени.

  • vim.async.iter() — даёт возможность получать результаты выполняющихся задач в порядке их фактического завершения, а не в порядке запуска.

  • vim.async.pawait() — является асинхронным аналогом pcall(). Вместо распространения ожидаемой ошибки вызывающий исходник получит флаг состояния, просто с сообщением об этой самой ошибке.

Выводы

Реакция сообщества на эти изменения была главным образом положительной. Единая асинхронная абстракция стала пластырем для такой болевой точки экосистемы как несовместимость плагинов, вызванная конкурирующими сторонними библиотеками. Конечно, дискуссии по решению тоже есть, например вызывают вопросы нюансы распространения ошибок и, например, само взаимодействие vim.async.await() с Libuv, тем не менее модификация явно решает проблемы многих разработчиков, а это самое главное.

Читают сейчас

Claude по одному промпту и за некоторое количество переделок создала печатную плату в KiCad с нуля, ПО для MCU тоже создал текст ИИ

7 часов назад

Claude по одному промпту и за некоторое количество переделок создала печатную плату в KiCad с нуля, ПО для MCU тоже создал текст ИИ

Потребитель Reddit под ником a6m–zero рассказал, что решил с помощью Claude Fable 5 практически за один промпт спроектировать печатную плату с нуля, причём от схемы и ПО до готового устройства. В проц

Глава Anthropic призвал замедлить развитие ИИ

9 часов назад

Глава Anthropic призвал замедлить развитие ИИ

Глава Anthropic Дарио Амодеи опубликовал эссе We Must Pace the Frontier, в котором предложил замедлить увеличение возможностей передовых ИИ-моделей. По его мнению, исследования безопасности перестают

Выпуск OpenRGB 1.0 — открытого проекта для управления RGB-подсветкой периферии

12 часов назад

Выпуск OpenRGB 1.0 — открытого проекта для управления RGB-подсветкой периферии

12 сентября 2026 года состоялся первый мажорный выпуск открытой утилиты OpenRGB. Проект даёт возможность управлять RGB-подсветкой материнских плат, видеокарт, клавиатур, кулеров, светодиодных лент и д

Что нового в PHPUnit 13.3

14 часов назад

Что нового в PHPUnit 13.3

PHPUnit на данный момент является де-факто стандартом для написания тестов в экосистеме PHP. Он относится к семейству фреймворков xUnit (как JUnit или NUnit) и предоставляет инструменты для написания

Пополнение в семействах Zen 4 и Zen 3: новые процессоры Ryzen 5 7500 и Ryzen 5 5500F

15 часов назад

Пополнение в семействах Zen 4 и Zen 3: новые процессоры Ryzen 5 7500 и Ryzen 5 5500F

AMD без громких анонсов обновила официальный каталог продукции, добавив в него два новых 6-ядерных решения начального уровня: Ryzen™ 5 7500 на базе архитектуры Zen™ 4 и Ryzen™ 5 5500F на базе архитект