Вызов функций

Основной способ вызова других функций в Wippy. Выполнение зарегистрированных функций синхронно или асинхронно между процессами с полной поддержкой распространения контекста, учётных данных безопасности и таймаутов. Этот модуль центральный для построения распределённых приложений, где компоненты должны взаимодействовать.

Загрузка

local funcs = require("funcs")

call

Синхронный вызов зарегистрированной функции. Используйте, когда нужен немедленный результат и можете его ждать.

local result, err = funcs.call("app.api:get_user", user_id)
if err then
    return nil, err
end
print(result.name)
Параметр Тип Описание
target string ID функции в формате "namespace:name"
...args any Аргументы, передаваемые функции

Возвращает: result, error

Строка target следует паттерну namespace:name, где namespace идентифицирует модуль, а name — конкретную функцию.

async

Запускает асинхронный вызов функции и немедленно возвращает Future. Используйте для долгих операций, где не хотите блокироваться, или когда хотите запустить несколько операций параллельно.

-- Запуск тяжёлых вычислений без блокировки
local future, err = funcs.async("app.process:analyze_data", large_dataset)
if err then
    return nil, err
end

-- Делаем другую работу пока вычисления идут...

-- Ждём результат когда готовы
local ch = future:response()
local payload, ok = ch:receive()
if ok then
    local result = payload:data()
end
Параметр Тип Описание
target string ID функции в формате "namespace:name"
...args any Аргументы, передаваемые функции

Возвращает: Future, error

new

Создаёт новый Executor для построения вызовов с пользовательским контекстом. Используйте, когда нужно распространить контекст запроса, установить учётные данные безопасности или настроить таймауты.

local exec = funcs.new()

Возвращает: Executor, error

Executor

Builder для вызовов функций с опциями контекста. Методы возвращают новые экземпляры Executor (иммутабельная цепочка), так что можно переиспользовать базовую конфигурацию.

with_context

Добавляет значения контекста, которые будут доступны вызываемой функции. Используйте для распространения данных уровня запроса: trace ID, пользовательских сессий, feature flags.

-- Распространение контекста запроса в downstream-сервисы
local exec = funcs.new():with_context({
    request_id = ctx.get("request_id"),
    feature_flags = {dark_mode = true}
})

local user, err = exec:call("app.api:get_user", user_id)
Параметр Тип Описание
values table Пары ключ-значение для добавления в контекст

Возвращает: Executor, error

with_actor

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

local security = require("security")
local actor = security.actor()  -- Получить актора текущего пользователя

-- Вызов админской функции с учётными данными пользователя
local exec = funcs.new():with_actor(actor)
local result, err = exec:call("app.admin:delete_record", record_id)
if err and err:kind() == "PERMISSION_DENIED" then
    return nil, errors.new("PERMISSION_DENIED", "User cannot delete records")
end
Параметр Тип Описание
actor Actor Актор безопасности (из модуля security)

Возвращает: Executor, error

with_scope

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

local security = require("security")
local scope = security.new_scope()

local exec = funcs.new():with_scope(scope)
Параметр Тип Описание
scope Scope Область безопасности (из модуля security)

Возвращает: Executor, error

with_options

Устанавливает опции вызова: таймаут и приоритет. Используйте для операций, которым нужны временные ограничения.

-- Установить таймаут 5 секунд для вызова внешнего API
local exec = funcs.new():with_options({timeout = 5000})
local result, err = exec:call("app.external:fetch_data", query)
if err then
    -- Обработать таймаут или другую ошибку
end
Параметр Тип Описание
options table Опции, специфичные для реализации

Возвращает: Executor, error

call / async

Версии Executor для call и async, использующие настроенный контекст.

-- Создать переиспользуемый executor с контекстом
local exec = funcs.new()
    :with_context({trace_id = "abc-123"})
    :with_options({timeout = 10000})

-- Несколько вызовов с тем же контекстом
local users, _ = exec:call("app.api:list_users")
local posts, _ = exec:call("app.api:list_posts")

Future

Возвращается вызовами async(). Представляет выполняющуюся асинхронную операцию.

response / channel

Возвращает базовый канал для получения результата.

local future, _ = funcs.async("app.api:slow_operation", data)
local ch = future:response()  -- или future:channel()

local result = channel.select {
    ch:case_receive(),
    timeout:case_receive()
}

Возвращает: Channel

is_complete

Неблокирующая проверка завершённости future.

while not future:is_complete() do
    -- делаем другую работу
    time.sleep("100ms")
end
local result, err = future:result()

Возвращает: boolean

is_canceled

Возвращает true если cancel() был вызван на этом future.

if future:is_canceled() then
    print("Operation was canceled")
end

Возвращает: boolean

result

Возвращает закэшированный результат если завершён, или nil если ещё выполняется.

local value, err = future:result()
if err then
    print("Failed:", err:message())
elseif value then
    print("Got:", value:data())
end

Возвращает: Payload|nil, error|nil

error

Возвращает ошибку если future завершился неудачей.

local err, has_error = future:error()
if has_error then
    print("Error kind:", err:kind())
end

Возвращает: error|nil, boolean

cancel

Отменяет асинхронную операцию.

future:cancel()

Параллельные операции

Запуск нескольких операций конкурентно с async и channel.select.

-- Запуск нескольких операций параллельно
local f1, _ = funcs.async("app.api:get_user", user_id)
local f2, _ = funcs.async("app.api:get_orders", user_id)
local f3, _ = funcs.async("app.api:get_preferences", user_id)

-- Ждём завершения всех через каналы
local user_ch = f1:channel()
local orders_ch = f2:channel()
local prefs_ch = f3:channel()

local results = {}
for i = 1, 3 do
    local r = channel.select {
        user_ch:case_receive(),
        orders_ch:case_receive(),
        prefs_ch:case_receive()
    }
    if r.channel == user_ch then
        results.user = r.value:data()
    elseif r.channel == orders_ch then
        results.orders = r.value:data()
    else
        results.prefs = r.value:data()
    end
end

Разрешения

Операции с функциями подчиняются вычислению политики безопасности.

Action Resource Описание
funcs.call ID функции Вызов конкретной функции
funcs.context context Использовать with_context() для установки контекста
funcs.security security Использовать with_actor() или with_scope()

Ошибки

Условие Kind Повторяемо
Target пустой errors.INVALID нет
Отсутствует namespace errors.INVALID нет
Отсутствует name errors.INVALID нет
Разрешение отклонено errors.PERMISSION_DENIED нет
Подписка не удалась errors.INTERNAL нет
Ошибка функции varies varies

См. Обработка ошибок для работы с ошибками.