Руководство разработчика 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.

При использовании объявления async function или простых обещаний JavaScript в версии 2.x, 3.x или 4.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);
    };
    

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

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

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

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

    module.exports = async function(context) {
        let retMsg = 'Hello, world!';
        return {
            httpResponse: {
                body: retMsg
            },
            queueOutput: retMsg
        };
    };
    
  • [Рекомендуется для одного экземпляра выходных данных.] Возвращает значение напрямую и с использованием имени привязки $return. Этот метод работает для функций возврата (асинхронной или функции обещаний). См. пример в разделе Экспорт асинхронной функции.

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

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

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

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

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

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

Объект context

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

module.exports = async function(context){

    // function logic goes here

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

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

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

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

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

Свойство 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

В 2.x, 3.x и 4.x функция должна быть помечена как асинхронная, даже если в функции нет ожидающего вызова функции, при этом функции не нужно вызывать context.done, чтобы указать конец функции.

//you don't need an awaited function call inside to use async
module.exports = async function (context, req) {
    context.log("you don't need an awaited function call inside to use async")
};

Метод 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 для записи в консоль.

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

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

Метод Описание
context.log.error(message) Записывает событие уровня ошибки в журналы.
context.log.warn(message) Записывает событие уровня предупреждения в журналы.
context.log.info(message) Записывает сообщение в журнал на уровне сведений или более низком.
context.log.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 = async 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});
};

Параметр 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() вызывается неявным образом.

  • [Только ответ] Путем возврата ответа. Специальное имя привязки $return позволяет назначить возвращаемое значение функции выходной привязке. Следующая привязка вывода HTTP определяет параметр вывода $return:

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

    В функции 2.x+ можно вернуть объект ответа напрямую:

    return { status: 201, body: "Insert succeeded." };
    

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

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

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

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

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

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

Версия узла

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

Версия службы "Функции" Версия узла (Windows) Версия узла (Linux)
4.x (рекомендуется) ~18(предварительная версия)
~16
~14
node|18(предварительная версия)
node|16
node|14
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, например, ~16.

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

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

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

// Import the underscore.js library
const _ = require('underscore');

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

Примечание

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

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

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

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

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

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

Использование Kudu (только Windows)

  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 в настоящее время являются предварительной версией функции в Функциях Azure Node.js 14 и 16.

Модули 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;
    };

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

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.

Примечание

Вы можете настроить languageWorkers:node:arguments только при локальном запуске приложения-функции.

Тестирование

Тестирование функций включает в себя:

  • Сквозной HTTP: чтобы протестировать функцию из конечной точки HTTP, можно использовать любое средство, которое может выполнять HTTP-запрос, например cURL, Postman или метод получения JavaScript.

  • Тестирование интеграции: тест интеграции включает слой приложения-функции. Это тестирование означает, что необходимо управлять параметрами функции, включая запрос и контекст. Контекст уникален для каждого типа триггера и означает, что необходимо знать входящие и исходящие привязки для этого типа триггера.

    Узнайте больше о тестировании интеграции и макетировании слоя контекста с помощью экспериментального репозитория GitHub, https://github.com/anthonychu/azure-functions-test-utils.

  • Модульное тестирование: модульное тестирование выполняется в приложении-функции. Вы можете использовать любое средство, которое может тестировать JavaScript, например Jest или Mocha.

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() 1.x за пределами области функции обратного вызова означает, что вызов функции может завершиться до того, как будет прочитан файл (проблема 2). В этом примере слишком ранний вызов context.done() 1.x приводит к непопаданию в журнал записей начиная с 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}`);
}

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

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