Руководство разработчика JavaScript для Функций Azure

В данном руководстве содержатся подробные сведения, полезные для разработчиков функций Azure с помощью JavaScript.

Разработчикам Express.js, Node.js и JavaScript, которые еще не знакомы с Функциями Azure, рекомендуется сначала ознакомиться с одной из следующих статей:

Начало работы Основные понятия Направляемое обучение

Основы функций JavaScript

Функция JavaScript (Node.js) — это экспортированный объект function, который выполняется при активации (триггеры настраиваются в файле function.json). Первый аргумент каждой функции передается объекту context, который используется для получения и отправки данных привязки, ведения журналов и взаимодействия со средой выполнения.

Структура папок

Необходимая структура папок для проекта JavaScript выглядит следующим образом. Это значение по умолчанию можно изменить. Дополнительные сведения см. в разделе о scriptFile ниже.

FunctionsProject
 | - MyFirstFunction
 | | - index.js
 | | - function.json
 | - MySecondFunction
 | | - index.js
 | | - function.json
 | - SharedCode
 | | - myFirstHelperFunction.js
 | | - mySecondHelperFunction.js
 | - node_modules
 | - host.json
 | - package.json
 | - extensions.csproj

В корневой папке проекта существует общий файл host.json, который может использоваться для настройки приложения-функции. У каждой функции есть папка с собственным файлом кода (JS) и файлом конфигурации привязки (function.json). Имя родительского каталога файла function.json всегда является именем этой функции.

Расширения привязки, необходимые в версии 2.x среды выполнения функций, определены в файле extensions.csproj с фактическими файлами библиотеки в папке bin. При локальной разработке необходимо зарегистрировать расширения привязки. При разработке функций на портале Azure эта регистрация выполняется автоматически.

Экспорт функции

Функции JavaScript должны экспортироваться через module.exports (или exports). Экспортированная функция должна быть функцией JavaScript, которая выполняется при активации.

По умолчанию среда выполнения Функций ищет функцию в файле index.js, где index.js использует тот же родительский каталог, что и соответствующий файл function.json. В стандартном случае экспортированная функция должна быть единственным экземпляром экспорта из соответствующего файла (экспорта с именем run или index). Чтобы настроить расположение файла и имя экспорта функции, ознакомьтесь с разделом ниже о настройке точки входа функции.

Экспортированной функции передается число аргументов при выполнении. Первый аргумент, который она всегда принимает, — это объект context. Если функция является синхронной (не возвращает обещание), необходимо передать объект context, так как для корректного использования требуется вызов context.done.

// You should include context, other arguments are optional
module.exports = function(context, myTrigger, myInput, myOtherInput) {
    // function logic goes here :)
    context.done();
};

Экспорт асинхронной функции

При использовании объявленияasync function или простых обещаний JavaScript в версии 2.х среды выполнения Функций вам не нужно явно вызывать context.done, чтобы сообщить, что функция завершена. Ваша функция завершается при завершении экспортированной асинхронной функции или обещания. Для функций, предназначенных для среды выполнения версии 1.x, по-прежнему необходимо вызвать context.done после выполнения кода.

Следующий пример — это простая функция, которая записывает в журнал, что она была запущена, и немедленно завершает выполнение.

module.exports = async function (context) {
    context.log('JavaScript trigger function processed a request.');
};

При экспорте асинхронной функции вы также можете настроить выходные привязки, чтобы принять значение return. Мы рекомендуем выполнять этот шаг, если существует только одна выходная привязка.

Чтобы назначить выходные данные с помощью return, измените свойство name на $return в function.json.

{
  "type": "http",
  "direction": "out",
  "name": "$return"
}

В этом случае функция должна выглядеть следующим образом:

module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');
    // You can call and await an async method here
    return {
        body: "Hello, world!"
    };
}

Привязки

В JavaScript привязки настраиваются и определяются в файле function.json функции. Функции взаимодействуют с привязками несколькими способами.

Входные данные

Входные данные в Функциях Azure делятся на две категории: входные данные от триггера и дополнительные входные данных. Привязки триггера и другие привязки для ввода (привязки direction === "in") могут считываться функцией тремя способами.

  • [Рекомендуется.] Как параметры, передаваемые функции. Они передаются в функцию в том же порядке, в каком они определены в файле function.json. Свойство name, определенное в файле function.json, не должно совпадать с именем параметра, хотя и может.

    module.exports = async function(context, myTrigger, myInput, myOtherInput) { ... };
    
  • Как элементы объекта context.bindings. Каждый элемент назван по свойству name, определенному в файле function.json.

    module.exports = async function(context) { 
        context.log("This is myTrigger: " + context.bindings.myTrigger);
        context.log("This is myInput: " + context.bindings.myInput);
        context.log("This is myOtherInput: " + context.bindings.myOtherInput);
    };
    
  • В качестве входных данных при использовании объекта JavaScript arguments. Этот способ по сути идентичен передаче входных данных в качестве параметров, но позволяет динамически обрабатывать входные данные.

    module.exports = async function(context) { 
        context.log("This is myTrigger: " + arguments[1]);
        context.log("This is myInput: " + arguments[2]);
        context.log("This is myOtherInput: " + arguments[3]);
    };
    

Выходные данные

Выходные данные (привязки direction === "out") могут быть записаны в функцию несколькими способами. Во всех случаях свойство name привязки, определенное в файле function.json, соответствует имени элемента объекта, записанного в функции.

Вы можете назначить данные выходным привязкам одним из следующих способов (не используйте их вместе).

  • [Рекомендуется, если существует несколько экземпляров выходных данных.] Возврат объекта. Если вы используете функцию возврата (асинхронную или функцию обещаний), вы можете возвращать объект с назначенными выходными данными. В приведенном ниже примере выходные привязки в файле function.json называются httpResponse и queueOutput.

    module.exports = async function(context) {
        let retMsg = 'Hello, world!';
        return {
            httpResponse: {
                body: retMsg
            },
            queueOutput: retMsg
        };
    };
    

    Если вы используете синхронную функцию, вы можете вернуть этот объект с помощью context.done (см. пример).

  • [Рекомендуется для одного экземпляра выходных данных.] Возвращает значение напрямую и с использованием имени привязки $return. Этот метод работает для функций возврата (асинхронной или функции обещаний). См. пример в разделе Экспорт асинхронной функции.

  • Присвоение значений для свойства context.bindings. Вы можете присвоить значения непосредственно для context.bindings.

    module.exports = async function(context) {
        let retMsg = 'Hello, world!';
        context.bindings.httpResponse = {
            body: retMsg
        };
        context.bindings.queueOutput = retMsg;
        return;
    };
    

Тип привязки данных

Чтобы определить тип данных для входной привязки, используйте свойство dataType в определении привязки. Например, чтобы прочитать содержимое HTTP-запроса в двоичном формате, используйте тип binary:

{
    "type": "httpTrigger",
    "name": "req",
    "direction": "in",
    "dataType": "binary"
}

Параметры для dataType — это binary, stream и string.

Объект context

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

Для функций, в которых реализован синхронный код, объект контекста включает обратный вызов done, который инициируется при завершении обработки функции. При написании асинхронного кода явный вызов done не требуется: обратный вызов done инициируется неявно.

module.exports = (context) => {

    // function logic goes here

    context.log("The function has executed.");

    context.done();
};

Контекст, переданный в функцию, предоставляет свойство executionContext, которое является объектом со следующими свойствами:

Имя свойства Тип Описание
invocationId Строка Предоставляет уникальный идентификатор для конкретного вызова функции.
functionName Строка Предоставляет имя выполняемой функции.
functionDirectory Строка Предоставляет каталог приложения-функций.

В следующем примере показано, как вернуть invocationId.

module.exports = (context, req) => {
    context.res = {
        body: context.executionContext.invocationId
    };
    context.done();
};

Свойство context.bindings

context.bindings

Возвращает именованный объект, используемый для чтения или установки данных привязки. Для доступа к данным входных и триггеров следует считывать свойства в context.bindings. Данные выходной привязки могут назначаться путем добавления в объект context.bindings.

Например, следующие определения привязки в function.json позволяют вам получить доступ к содержимому очереди из context.bindings.myInput и назначить выходные данные очереди, используя context.bindings.myOutput.

{
    "type":"queue",
    "direction":"in",
    "name":"myInput"
    ...
},
{
    "type":"queue",
    "direction":"out",
    "name":"myOutput"
    ...
}
// myInput contains the input data, which may have properties such as "name"
var author = context.bindings.myInput.name;
// Similarly, you can set your output data
context.bindings.myOutput = { 
        some_text: 'hello world', 
        a_number: 1 };

Вы можете определить выходные данные привязки с помощью метода context.done вместо объекта context.binding (см. ниже).

Свойство context.bindingData

context.bindingData

Это свойство возвращает именованный объект, содержащий метаданные триггера и данные вызова функции (invocationId, sys.methodName, sys.utcNow, sys.randGuid). Пример метаданных триггера см. по этой ссылке с примером для концентраторов событий.

Метод context.done

Метод context. Done используется синхронными методами.

Синхронное выполнение Асинхронное выполнение
(Node 8 +, среда выполнения функций 2 +)
Требуется: context.done([err],[propertyBag]) для информирования среды выполнения о завершении функции. Если этот метод отсутствует, истекает время ожидания выполнения.
Метод context.done позволяет передавать в среду выполнения и определяемые пользователем сообщения об ошибке, и объект JSON, содержащий выходные данные привязки. Свойства, переданные в context.done, перезапишут любые значения, заданные для объекта context.bindings.
Не требуется: context.done -он вызывается неявно.
// Synchronous code only
// Even though we set myOutput to have:
//  -> text: 'hello world', number: 123
context.bindings.myOutput = { text: 'hello world', number: 123 };
// If we pass an object to the done function...
context.done(null, { myOutput: { text: 'hello there, world', noNumber: true }});
// the done method overwrites the myOutput binding to be: 
//  -> text: 'hello there, world', noNumber: true

Метод context.log

context.log(message)

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

Запись выходных данных трассировки в журналы

В Функциях Azure для записи выходных данных трассировки в журналы и на консоль применяются методы context.log. При вызове context.log() ваше сообщение записывается в журнал на уровне трассировки по умолчанию (уровне сведений). Функции Azure интегрируются с Azure Application Insights для более эффективного ведения журналов в приложениях-функциях. Application Insights, компонент Azure Monitor, содержит инструменты для сбора, визуального отображения и анализа телеметрии приложений и выходных данных трассировки. Дополнительные сведения см. в статье Мониторинг Функций Azure.

В примере ниже ведется журнал на уровне трассировки сведений, включая идентификатор вызова:

context.log("Something has happened. " + context.invocationId); 

Все методы context.log поддерживают тот же формат параметров, что и метод Node.js util.format. Просмотрите следующий код, который выполняет запись в журналы функций, используя уровень трассировки по умолчанию:

context.log('Node.js HTTP trigger function processed a request. RequestUri=' + req.originalUrl);
context.log('Request Headers = ' + JSON.stringify(req.headers));

Этот же код можно записать в таком формате:

context.log('Node.js HTTP trigger function processed a request. RequestUri=%s', req.originalUrl);
context.log('Request Headers = ', JSON.stringify(req.headers));

Примечание

Не используйте console.log для записи выходных данных трассировки. Поскольку выходные данные из console.log перехватываются на уровне приложения-функции, они не привязаны к конкретному вызову функции и не отображаются в ее журналах. Кроме того, версия 1. x среды выполнения функций не поддерживает использование метода console.log для записи в консоль.

Уровни трассировки

Помимо уровня по умолчанию доступны следующие методы ведения журнала, позволяющие формировать журналы функций на тех или иных уровнях трассировки.

Метод Описание
error(message) Записывает событие уровня ошибки в журналы.
warn(message) Записывает событие уровня предупреждения в журналы.
info(message) Записывает сообщение в журнал на уровне сведений или более низком.
verbose(message) Записывает сообщение в журнал на уровне детализации.

В следующем примере тот же журнал записывается на уровне трассировки предупреждений, а не на уровне сведений:

context.log.warn("Something has happened. " + context.invocationId); 

Так как уровень ошибок является наивысшим уровнем трасировки, эта трассировка записывается в выходные данные на всех уровнях трассировки при условии, что ведение журнала включено.

Настройка уровня трассировки для ведения журнала

В Функциях Azure можно задать пороговое значение уровня трассировки для записи в консоль. Определенные пороговые значения зависят от версии среды выполнения Функций.

Задать пороговое значение для всех данных трассировки, которые записываются в журнал, можно с помощью свойства logging.logLevel в файле host.json. Этот объект JSON позволяет задать пороговое значение по умолчанию для всех функций в приложении-функции, а также указать определенные пороговые значения для отдельных функций. Дополнительные сведения см. в статье Настройка мониторинга для Функций Azure.

Ведение журнала пользовательской телеметрии

По умолчанию Функции записывают выходные данные в виде журналов трассировки в Application Insights. Для более полного контроля можно использовать пакет SDK для Node.js Application Insights, позволяющий отправлять пользовательские данные телеметрии в экземпляр Application Insights.

const appInsights = require("applicationinsights");
appInsights.setup();
const client = appInsights.defaultClient;

module.exports = function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    // Use this with 'tagOverrides' to correlate custom telemetry to the parent function invocation.
    var operationIdOverride = {"ai.operation.id":context.traceContext.traceparent};

    client.trackEvent({name: "my custom event", tagOverrides:operationIdOverride, properties: {customProperty2: "custom property value"}});
    client.trackException({exception: new Error("handled exceptions can be logged with this method"), tagOverrides:operationIdOverride});
    client.trackMetric({name: "custom metric", value: 3, tagOverrides:operationIdOverride});
    client.trackTrace({message: "trace message", tagOverrides:operationIdOverride});
    client.trackDependency({target:"http://dbname", name:"select customers proc", data:"SELECT * FROM Customers", duration:231, resultCode:0, success: true, dependencyTypeName: "ZSQL", tagOverrides:operationIdOverride});
    client.trackRequest({name:"GET /customers", url:"http://myserver/customers", duration:309, resultCode:200, success:true, tagOverrides:operationIdOverride});

    context.done();
};

Параметр tagOverrides присваивает параметру operation_Id значение идентификатора вызова функции. Этот параметр позволяет сопоставлять все автоматически создаваемые и пользовательские данные телеметрии с конкретным вызовом функции.

Триггеры и привязки HTTP

Триггеры HTTP и webhook, а также привязки вывода HTTP используют объекты запроса и ответа для обмена сообщениями HTTP.

Объект запроса

У объекта (запроса) context.req есть следующие свойства:

Свойство Описание
body Объект, содержащий текст запроса.
headers Объект, содержащий заголовок запроса.
method Метод HTTP, используемый для запроса.
originalUrl URL-адрес запроса.
params Объект, содержащий параметры маршрутизации запроса.
запрос Объект, содержащий параметры запроса.
rawBody Текст сообщения в виде строки.

Объект ответа

У объекта (ответа) context.res есть следующие свойства:

Свойство Описание
body Объект, содержащий текст ответа.
headers Объект, содержащий заголовок ответа.
isRaw Указывает, что форматирование пропускается для ответа.
status Код состояния HTTP ответа.
Файлы cookie Массив HTTP-объектов cookie, заданных в ответе. У HTTP-объекта cookie есть name, value и другие свойства файлов cookie, такие как maxAge или sameSite.

Доступ к запросу и ответу

При работе с триггерами HTTP вы можете получить доступ к объектам запроса и ответа HTTP одним из нескольких способов.

  • От свойств req и res объекта context. В этом случае можно использовать обычный шаблон доступа к данным HTTP из объекта context вместо полного шаблона context.bindings.name. Следующий пример демонстрирует доступ к объектам req и res в context:

    // You can access your HTTP request off the context ...
    if(context.req.body.emoji === ':pizza:') context.log('Yay!');
    // and also set your HTTP response
    context.res = { status: 202, body: 'You successfully ordered more coffee!' }; 
    
  • С помощью именованных входных и выходных привязок. В этом случае триггер и привязки HTTP работают как любая другая привязка. В следующем примере объект ответа задается с помощью именованной привязки response:

    {
        "type": "http",
        "direction": "out",
        "name": "response"
    }
    
    context.bindings.response = { status: 201, body: "Insert succeeded." };
    
  • [Только ответ.] Путем вызова context.res.send(body?: any). HTTP-ответ создается с входными данными body в качестве текста ответа. context.done() вызывается неявным образом.

  • [Только ответ.] Путем вызова context.done(). Специальный тип привязки HTTP возвращает ответ, который передается методу context.done(). Следующая привязка вывода HTTP определяет параметр вывода $return:

    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
    
     // Define a valid response object.
    res = { status: 201, body: "Insert succeeded." };
    context.done(null, res);   
    

Обратите внимание на то, что ключи запроса и ответа указаны в нижнем регистре.

Масштабирование и параллелизм

По умолчанию Функции Azure автоматически отслеживают нагрузку на приложение и при необходимости создают дополнительные экземпляры узлов для Node.js. Функции используют встроенные пороговые значения (недоступные для настройки пользователем) для различных типов триггеров, чтобы решить, когда следует добавлять экземпляры (например, возраст сообщений и размер очереди для QueueTrigger). Дополнительные сведения см. в статье, посвященной планам с оплатой по мере использования и планам "Премиум".

Такого поведения при масштабировании достаточно для многих приложений Node.js. Для приложений, зависящих от ЦП, производительность можно повысить путем увеличения числа рабочих процессов обработки языка.

По умолчанию каждый экземпляр узла функций имеет рабочий процесс с одним языком. Вы можете увеличить количество рабочих процессов на узел (до 10) с помощью параметра приложения FUNCTIONS_WORKER_PROCESS_COUNT. Затем Функции Azure пытаются равномерно распределять одновременные вызовы функций между этими рабочими процессами.

FUNCTIONS_WORKER_PROCESS_COUNT применяется к каждому узлу, создаваемому функциями при масштабировании приложения для удовлетворения потребности.

Версия узла

В таблице ниже приведены текущие поддерживаемые версии Node.js для каждой основной версии среды выполнения функций по операционным системам.

Версия службы "Функции" Версия узла (Windows) Версия узла (Linux)
3.x (рекомендуется) ~14 (рекомендуется)
~12
~10
node|14 (рекомендуется)
node|12
node|10
2.x ~12
~10
~8
node|10
node|8
1.x 6.11.2 (заблокировано средой выполнения) Недоступно

Зафиксировав параметр process.version из любой функции, можно увидеть текущую версию, которую использует среда выполнения.

Установка версии Node.js

Для приложений-функций Windows можно выбрать целевую версию Azure, задав для параметра приложения WEBSITE_NODE_DEFAULT_VERSION поддерживаемую версию LTS, например ~14.

Для приложений-функций Linux обновить версию Node можно с помощью следующей команды Azure CLI.

az functionapp config set --linux-fx-version "node|14" --name "<MY_APP_NAME>" --resource-group "<MY_RESOURCE_GROUP_NAME>"

Дополнительные сведения о политике поддержки среды выполнения в Функциях Azure см. в этой статье.

Управление зависимостями

Чтобы использовать библиотеки сообщества в коде JavaScript, как показано в следующем примере, необходимо убедиться, что установлены все зависимости приложения-функции в Azure.

// Import the underscore.js library
var _ = require('underscore');
var version = process.version; // version === 'v6.5.0'

module.exports = function(context) {
    // Using our imported underscore.js library
    var matched_names = _
        .where(context.bindings.myInput.names, {first: 'Carla'});

Примечание

Следует определить файл package.json в корне вашего приложения-функции. После этого все функции в приложении будут совместно использовать одни и те же кэшированные пакеты, что обеспечивает наилучшую производительность. При возникновении конфликтов версий их можно разрешить, добавив файл package.json в папку определенной функции.

При развертывании приложения-функции из системы управления версиями любой файл package.json, присутствующий в вашем репозитории, вызовет npm install в своей папке во время развертывания. Но при развертывании с помощью портала или интерфейса командной строки вам придется вручную устанавливать пакеты.

Существует два способа установки пакетов в приложение-функцию.

Развертывание с зависимостями

  1. Установите все необходимые пакеты в локальной среде, запустив npm install.

  2. Разверните свой код и убедитесь, что папка node_modules включена в развертывание.

С помощью Kudu

  1. Перейдите к https://<function_app_name>.scm.azurewebsites.net.

  2. Щелкните Debug Console (Консоль отладки) > CMD.

  3. Перейдите к D:\home\site\wwwroot, а затем перетащите файл package.json в папку wwwroot в верхней части страницы.
    Существуют другие способы передачи файлов в приложение-функцию. Дополнительные сведения см. в разделе Как обновить файлы приложения-функции.

  4. После загрузки файла package.json запустите команду npm install в консоли удаленного выполнения Kudu.
    В результате этого действия будут загружены пакеты, указанные в файле package.json, и перезапущено приложение-функция.

Переменные среды

Вы можете добавить в приложение-функцию собственные переменные среды как в локальной, так и в облачной средах — например, это могут быть рабочие секреты (строки подключения, ключи и конечные точки) или параметры среды (например, переменные профилирования). Для доступа к этим параметрам используйте process.env в коде функции.

В локальной среде разработки

При работе в локальной среде проект функций содержит файл local.settings.json, в котором переменные среды хранятся в объекте Values.

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "translatorTextEndPoint": "https://api.cognitive.microsofttranslator.com/",
    "translatorTextKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "languageWorkers__node__arguments": "--prof"
  }
}

В облачной среде Azure

При работе в Azure приложение функции позволяет задавать и использовать Параметры приложения, такие как строки подключения службы, и предоставляет эти параметры в виде переменных среды во время выполнения.

Существует несколько способов для добавления, обновления и удаления параметров приложения-функции.

После изменения параметров приложения-функции нужно перезапустить приложение-функцию.

Доступ к переменным среды из кода

Для доступа к параметрам приложения как к переменным среды можно использовать process.env, как показано здесь во втором и третьем вызовах к context.log(), где мы заносим в журнал переменные среды AzureWebJobsStorage и WEBSITE_SITE_NAME:

module.exports = async function (context, myTimer) {

    context.log("AzureWebJobsStorage: " + process.env["AzureWebJobsStorage"]);
    context.log("WEBSITE_SITE_NAME: " + process.env["WEBSITE_SITE_NAME"]);
};

Модули ECMAScript (предварительная версия)

Примечание

Поскольку модули ECMAScript в настоящее время помечены как экспериментальные в Node.js версии 14, они доступны в режиме предварительной версии в Функциях Azure для Node.js 14. Пока поддержка Node.js 14 для модулей ECMAScript не будет переведена в стабильный режим, API и поведение этих компонентов могут измениться.

Модули ECMAScript (модули ES) — это новая официальная стандартная система модулей для Node.js. Пока что в примерах кода в этой статье используется синтаксис CommonJS. При выполнении Функций Azure в Node.js 14 вы можете использовать в них синтаксис модулей ES.

Чтобы использовать в функции модули ES, переименуйте ее файл, заменив расширение на .mjs. Следующий пример файла index.mjs — это функция, активируемая через HTTP, которая использует синтаксис модулей ES для импорта библиотеки uuid и возвращает значение.

import { v4 as uuidv4 } from 'uuid';

export default async function (context, req) {
    context.res.body = uuidv4();
};

Настройка точки входа функции

Свойства function.json, а именно scriptFile и entryPoint, позволяют настроить расположение и имя экспортированной функции. Эти свойства важны, если JavaScript является транскомпилированным.

Использование scriptFile

По умолчанию функция JavaScript выполняется из файла index.js, который расположен в том же родительском каталоге, что и соответствующий файл function.json.

scriptFile можно использовать для получения структуры папок, которая выглядит следующим образом:

FunctionApp
 | - host.json
 | - myNodeFunction
 | | - function.json
 | - lib
 | | - sayHello.js
 | - node_modules
 | | - ... packages ...
 | - package.json

Файл function.json для myNodeFunction должен включать свойство scriptFile, указывающее на файл с экспортированной функцией, которую нужно выполнить.

{
  "scriptFile": "../lib/sayHello.js",
  "bindings": [
    ...
  ]
}

Использование entryPoint

В scriptFile (или index.js) функции должны экспортироваться с использованием module.exports, чтобы их можно было найти и запустить. По умолчанию функция, которая выполняется при запуске, является единственным экземпляром экспорта (с именем run или index) из этого файла.

Такой экспорт можно настроить, используя entryPoint в файле function.json, как показано в следующем примере:

{
  "entryPoint": "logFoo",
  "bindings": [
    ...
  ]
}

В среде Функций версии 2.x, которая поддерживает параметр this в пользовательских функциях, код этой самой функции может выглядеть следующим образом:

class MyObj {
    constructor() {
        this.foo = 1;
    };

    logFoo(context) { 
        context.log("Foo is " + this.foo); 
        context.done(); 
    }
}

const myObj = new MyObj();
module.exports = myObj;

В этом примере важно отметить, что несмотря на выполняемый экспорт объекта, нет никакой гарантии, что состояния между выполнениями будут сохранены.

Локальная отладка

При запуске с параметром --inspect процесс Node.js прослушивает клиент отладки на указанном порту. В Функциях Azure версии 2.x можно указать аргументы для передачи в процесс Node.js, который выполняет код, путем добавления переменной среды или параметра приложения languageWorkers:node:arguments = <args>.

Для локальной отладки добавьте "languageWorkers:node:arguments": "--inspect=5858" в раздел Values файла local.settings.json и подключите отладчик к порту 5858.

При отладке с помощью VS Code параметр --inspect автоматически добавляется в файл launch.json проекта со значением port.

В версии 1.x параметр languageWorkers:node:arguments не работает. Порт отладки можно выбрать с помощью параметра --nodeDebugPort в Azure Functions Core Tools.

TypeScript

Если вы используете версию 2.x среды выполнения Функций, то как Функции Azure для Visual Studio Code, так и Azure Functions Core Tools позволяют создавать приложения-функции на основе шаблона, поддерживающего проекты приложений-функций TypeScript. Этот шаблон создает файлы проекта package.json и tsconfig.json, облегчающие транскомпиляцию, запуск и публикацию функций JavaScript из кода TypeScript с помощью этих средств.

Созданный файл .funcignore позволяет указать, какие файлы следует исключить при публикации проекта в Azure.

Файлы TypeScript (TS) транскомпилируются в файлы JavaScript (JS) в выходном каталоге dist. В шаблонах TypeScript используется параметр scriptFile в разделе function.json, задающий расположение соответствующего JS-файла в папке dist. Расположение выходных данных задается в шаблоне с помощью параметра outDir в файле tsconfig.json. Если изменить этот параметр или имя папки, среда выполнения не сможет найти код для выполнения.

Способ локальной разработки и развертывания проекта TypeScript зависит от средства разработки.

Visual Studio Code

Расширение Функций Azure для Visual Studio Code позволяет разрабатывать функции с помощью TypeScript. Для работы с расширением Функций Azure требуется Core Tools.

Чтобы создать приложение-функцию TypeScript в Visual Studio Code, выберите на этапе создания язык TypeScript.

При нажатии клавиши F5 для локального запуска приложения транскомпиляция выполняется перед инициализацией узла (func.exe).

При развертывании приложения-функции в Azure с помощью кнопки развернуть в приложение-функцию... расширение Функций Azure сначала создает готовую к работе сборку файлов JavaScript из исходных файлов TypeScript.

Azure Functions Core Tools

При использовании Core Tools проект TypeScript отличается от проекта JavaScript в нескольких аспектах.

Создание проекта

Чтобы создать проект приложения-функции TypeScript с помощью Core Tools, необходимо на этапе создания выбрать язык TypeScript. Это можно сделать одним из следующих способов:

  • Выполните команду func init, выберите node в качестве языкового стека, а затем выберите typescript.

  • Выполните команду func init --worker-runtime typescript.

Локальное выполнение

Чтобы запустить код приложения-функции локально с помощью Core Tools, используйте вместо func host start следующие команды:

npm install
npm start

Команда npm start эквивалентна следующим командам:

  • npm run build
  • func extensions install
  • tsc
  • func start

Публикация в Azure

Прежде чем использовать команду [func azure functionapp publish] для развертывания проекта в Azure, создайте готовую к работе сборку файлов JavaScript из исходных файлов TypeScript.

Следующие команды подготавливают и публикуют проект TypeScript с помощью Core Tools:

npm run build:production 
func azure functionapp publish <APP_NAME>

В этом коде <APP_NAME> следует заменить именем приложения-функции.

Рекомендации для функций JavaScript

При работе с функциями JavaScript следует помнить о рекомендациях, приведенных в следующих разделах.

Выбор планов службы приложений для конфигурации с одним виртуальным ЦП

При создании приложения-функции, которое использует план службы приложения, мы советуем выбирать план для конфигурации с одним виртуальным ЦП вместо плана для нескольких виртуальных ЦП. Сейчас служба "Функции Azure" намного эффективнее выполняет функции JavaScript на виртуальных машинах с одним ЦП. Использование более крупных виртуальных машин не приводит к ожидаемому улучшению производительности. При необходимости вы можете вручную горизонтально увеличить масштаб дополнительных экземпляров виртуальных машин с одним ЦП или включить автоматическое масштабирование. Дополнительные сведения см. в статье Масштабирование числа экземпляров вручную или автоматически.

Холодный запуск

С разработкой Функций Azure в виде бессерверной модели размещения холодные запуски стали реальностью. Холодный запуск обозначает, что когда ваше приложение-функция запускается в первый раз после периода отсутствия активности, запуск займет больше времени. Для функций JavaScript, в частности с большими деревьями зависимостей, холодный запуск может оказаться достаточно длительным. Чтобы ускорить процесс холодного запуска, по возможности выполняйте функции в виде файла пакета. Многие методы развертывания используют эту модель по умолчанию, но если возникают большие временные задержки при холодном запуске и он не выполняется из файла пакета, запуск в виде файла пакета может значительно ускорить процесс.

Пределы подключения

При использовании зависящего от службы клиента в приложении Функций Azure не создавайте новый клиент при каждом вызове функции. Вместо этого создайте в глобальной области один статический клиент. Дополнительные сведения см. в статье Управление подключениями в Функциях Azure.

Использование async и await

При написании функций Azure на JavaScript в коде следует использовать ключевые слова async и await. Использование в коде async и await вместо обратных вызовов или .then и .catch с обещаниями помогает избежать двух распространенных проблем:

  • Возникновение неперехваченных исключений, которые приводят к аварийному завершению процесса Node.js, что может повлиять и на выполнение других функций.
  • Непредвиденное поведение, например пропуск журналов в context. log из-за неправильного ожидания асинхронных вызовов.

В примере ниже асинхронный метод fs.readFile вызывается с функцией обратного вызова ошибки в качестве второго параметра. Для этого кода возникают обе упомянутые выше проблемы. Исключение, которое не было явным образом перехвачено в правильной области, аварийно завершило работу всего процесса (проблема 1). Вызов context.done() за пределами области функции обратного вызова означает, что вызов функции может завершиться до того, как будет прочитан файл (проблема 2). В этом примере слишком ранний вызов context.done() приводит к непопаданию в журнал записей начиная с Data from file:.

// NOT RECOMMENDED PATTERN
const fs = require('fs');

module.exports = function (context) {
    fs.readFile('./hello.txt', (err, data) => {
        if (err) {
            context.log.error('ERROR', err);
            // BUG #1: This will result in an uncaught exception that crashes the entire process
            throw err;
        }
        context.log(`Data from file: ${data}`);
        // context.done() should be called here
    });
    // BUG #2: Data is not guaranteed to be read before the Azure Function's invocation ends
    context.done();
}

Ключевые слова async и await помогают избежать обеих этих ошибок. Для превращения функций ошибок (типа обратного вызова) в функции с поддержкой ожидания следует использовать служебную функцию util.promisify Node.js.

В примере ниже необработанные исключения, возникающие во время выполнения функции, приводят к завершению только того вызова, где они возникают. Ключевое слово await означает, что шаги за readFileAsync выполняются только после завершения readFile. С async и await также не требуется совершать обратный вызов context.done().

// Recommended pattern
const fs = require('fs');
const util = require('util');
const readFileAsync = util.promisify(fs.readFile);

module.exports = async function (context) {
    let data;
    try {
        data = await readFileAsync('./hello.txt');
    } catch (err) {
        context.log.error('ERROR', err);
        // This rethrown exception will be handled by the Functions Runtime and will only fail the individual invocation
        throw err;
    }
    context.log(`Data from file: ${data}`);
}

Дальнейшие действия

Дополнительные сведения см. в следующих ресурсах: