На головну | Розділ |
---|---|
<- Основні вузли | Робота з повідомленнями -> |
Вузол Function дозволяє виконувати код JavaScript для обробки повідомлень, що передаються через нього. Повідомлення передається у вигляді об'єкта з назвою msg
. За замовченням він має властивість msg.payload
що містить тіло повідомлення. Інші вузли можуть додавати до повідомлення власні властивості, які повинні бути описані у документації.
Код, введений у вузол Function -- це тіло функції. Найпростіша функція просто повертає повідомлення:
return msg;
Якщо функція повертає null
, тоді жодне повідомлення від вузла функції не передається далі, тобто потік на ньому закінчується. Повернений функцією об'єкт-повідомлення не обов'язково повинен бути таким самим типом об'єкту, який був переданий їй. Тобто функція може побудувати зовсім новий об'єкт, перш ніж повертати його. Наприклад:
var newMsg = { payload: msg.payload.length };
return newMsg;
Зауважте. Створюючи новий об'єкт msg
, повідомлення втрачає будь-які властивості того повідомлення, що було отримане на вході. Це призведе до руйнування деяких потоків, наприклад потік HTTP In/Response
вимагає щоб властивості msg.req
і msg.res
були збережені від кінця до кінця. Загалом, вузли Function мали б повертати об'єкт-повідомлення, вносячи будь-які зміни до його властивостей.
Діалог редагування Function дозволяє змінити кількість виходів. Якщо є більше одного виходу, для відправки на ці виходи повідомлень треба використати масив. Це полегшує написання функції, яка відправляє повідомлення на різні виходи в залежності від певного стану. Наприклад, ця функція буде надсилати що-небудь на тему (topic) banana
до другого виходу, а не до першого
if (msg.topic === "banana") {
return [ null, msg ];
} else {
return [ msg, null ];
}
У наступному прикладі, оригінальне вхідне повідомлення передається на перший вихід, а на другий вихід передається повідомлення, яке містить довжину рядку payload:
var newMsg = { payload: msg.payload.length };
return [msg, newMsg];
Починаючи з Node-RED 1.3
node.outputCount
містить кількість виходів, налаштованих для функціонального вузла.
Це дає змогу писати загальні функції, які можуть обробляти змінну кількість виходів, встановлених у діалоговому вікні редагування. Наприклад, якщо ви бажаєте випадково розподілити вхідні повідомлення між виходами, ви можете:
// Створіть масив такої ж довжини, як і кількість виходів
const messages = new Array(node.outputCount)
// Виберіть випадковий вихідний номер, на який потрібно надіслати повідомлення
const chosenOutputIndex = Math.floor(Math.random() * node.outputCount);
// Надіслати повідомлення лише на вибраний вихід
messages[chosenOutputIndex] = msg;
// Повертає масив, що містить вибраний результат
return messages;
Тепер ви можете налаштувати кількість виходів лише за допомогою діалогового вікна редагування без внесення змін до самої функції.
Функція може повертати на виході послідовність з декількох повідомлень, повернувши масив повідомлень у середині іншого масиву. Наступні за ним вузли отримуватимуть повідомлення один за одним у тому порядку, в якому вони були у вихідному масиві цього вузла.
У наступному прикладі, послідовність повідомлень msg1
, msg2
, msg3
буде відправлена на перший вихід, а повідомлення msg4
буде відправлено на другий вихід.
var msg1 = { payload:"first out of output 1" };
var msg2 = { payload:"second out of output 1" };
var msg3 = { payload:"third out of output 1" };
var msg4 = { payload:"only message from output 2" };
return [ [ msg1, msg2, msg3 ], msg4 ];
У наступному прикладі отримане корисне навантаження (payload) розбивається на окремі слова, і повертається повідомлення, що вміщує кожне слово.
var outputMsgs = [];
var words = msg.payload.split(" ");
for (var w in words) {
outputMsgs.push({payload:words[w]});
}
return [ outputMsgs ];
Якщо функція повинна виконувати асинхронну дію (що не відразу повертає результат а потребує функцію зворотного виклику) що приймає участь у формуванні повідомлення, вона не може повернути повідомлення по завершенню обробки Function. Замість цього повинна використовуватися функція передачі повідомлень node.send()
. Наприклад:
doSomeAsyncWork(msg, function(result) {
msg.payload = result;
node.send(msg);
});
return;
Вузол функції буде клонувати кожен об’єкт повідомлення, який ви передаєте в node.send
, щоб уникнути ненавмисної модифікації об’єктів повідомлень, які повторно використовуються у функції. До версії Node-RED 1.0 вузол функцій не клонував перше повідомлення, передане в node.send
, а клонував тільки решту.
Функція може запитувати середовище виконання не клонувати перше повідомлення, передане в node.send
, передаючи в якості другого аргументу функції send
значення false
. Зрозуміло, що це повідомлення містить щось, що не може бути клоноване інакше, або з міркувань продуктивності, щоб мінімізувати накладні витрати на надсилання повідомлень:
node.send(msg,false);
Якщо вузол Function виконує асинхронну роботу з повідомленням, середовище виконання не буде автоматично знати, коли воно закінчило обробляти повідомлення.
Щоб допомогти це зробити, вузол Function повинен викликати node.done()
у відповідний час. Це дозволить виконувати належним чином відстеження повідомлень через систему.
doSomeAsyncWork(msg, function(result) {
msg.payload = result;
node.send(msg);
node.done();
});
return;
Починаючи версії 1.1.0, вузол Function забезпечує вкладку Setup
а з в 1.3 вона називається On Start
, де ви можете надати код, який запускатиметься при кожному запуску вузла. Це можна використовувати для налаштування будь-якого стану, потрібного вузлу функцій.
Наприклад, він може ініціалізувати значення в локальному контексті, які використовуватиме основна функція:
if (context.get("counter") === undefined) {
context.set("counter", 0)
}
Обробка Setup функції настройки може повернути Promise, якщо їй потрібно виконати асинхронну роботу до того, як основна функція може розпочати обробку повідомлень. Будь-які повідомлення, які надійдуть до завершення функції настройки, будуть вставлені в чергу і обробляться, коли вони будуть готові.
Аналогічно є вкладка On Stop
де виконується код перед повторним розгортанням Node-RED.
На вкладці "Setup" в вузлі "Function" в Node-RED налаштовуються залежності Node.js (пакети), які потрібні для виконання коду, написаного в цьому вузлі. В цьому розділі можна встановлювати, оновлювати або видаляти модулі, а також керувати версіями та залежностями за допомогою файлу package.json
.
Крім того, на вкладці "Setup" можна налаштувати середовище виконання коду, включаючи змінні оточення, доступні для використання в коді. Наприклад, ви можете встановити змінні середоивща для підключення до бази даних або інших зовнішніх служб.
Також на вкладці "Setup" можна налаштувати параметри виконання коду, наприклад, час очікування або ліміт пам'яті, які можуть бути корисні для оптимізації виконання коду в вузлі "Function".
У вузлі Function
на вкладці "Setup" міститься розділ "Modules". Цей розділ дозволяє встановлювати залежності Node.js (пакети) для використання в коді JavaScript, який ви пишете в цьому вузлі. Наприклад, якщо ви хочете використовувати сторонні бібліотеки або модулі у своєму коді JavaScript, ви можете встановити їх через цей розділ "Modules". Це може бути корисно, коли ви хочете використовувати більш складні або спеціалізовані функції, які не є доступними за замовчуванням у вузлі Function
. У розділі "Modules" ви можете встановлювати нові модулі, оновлювати вже встановлені модулі або видаляти модулі, які більше не потрібні. Крім того, ви можете використовувати спеціальний файл package.json
для керування залежностями та їх версіями. Це дозволяє зробити ваш код більш структурованим та керованим.
Якщо ви використовуєте асинхронний код зворотного виклику у своїх функціях, коли потік повторно розгортається, вам знадобиться прибирати будь-які непотрібні запити або закривати будь-які з'єднання. Ви можете зробити це, додавши обробник подій close
node.on('close', function() {
// tidy up any async code here - shutdown connections and so on.
});
Починаючи з Node-RED 1.1.0 цей код можна вписати в налаштуваннях закладки Close
вузла Функції абоOn Stop
у новіших функціях.
Якщо вузол повинен записувати щось в консоль, він може використовувати одну з наступних функцій:
node.log("Something happened");
node.warn("Something happened you should know about");
node.error("Oh no, something bad happened");
Повідомлення warn
і error
також надсилаються на вкладку налагодження редактора потоку. Для більш тонкорівневих подій також доступні node.trace()
і node.debug()
. Якщо для цих рівнів не налаштоване ведення журналу, вони відображатися не будуть.
Якщо функція виявляє помилку, вона припиняє поточний потік і нічого не повертає. Щоб запустити вузол Catch
для обробки помилок на тій самій вкладці, функція повинна викликати node.error
з оригінальним повідомленням в якості другого аргументу:
node.error("hit an error", msg);
Крім об'єкту msg
функція також може зберігати дані в контекстних сховищах. У вузлі Function є три заздалегідь визначені змінні, які можна використовувати для доступу до контексту:
-
context
- локальний контекст вузла -
flow
- контекст потоку -
global
- глобальний контекст
Зауважте. Ці заздалегідь означені змінні є особливостями вузла Function. Якщо ви створюєте спеціальний вузол, прочитайте Creating Nodes guide для ознайомлення з тим, як отримати доступ до контексту.
Наступні приклади використовують контекст потоку flow
, але аналогічно цей приклад застосовний і до context
, і до global
.
Існує два режими доступу до контексту: синхронний та асинхронний. Синхронний доступ означає, що виклик для отримання даних з сховища негайно повертає значення. Асинхронний доступ означає, що функція виклику для отримання даних також повинен містити функцію зворотного виклику, яка викликається після того, як значення стало доступним. Вбудовані сховища memory і file обидва пропонують синхронний доступ. Це означає, що існуючі (попередні до версії 0.19) потоки можуть використовувати ці сховища без будь-яких змін.
Щоб отримати значення з контексту використовується метод get
:
var myCount = flow.get("count");
Щоб встановити значення в контекст, використовується метод set
:
flow.set("count", 123);
У наведеному нижче прикладі зберігається лічильник кількості викликів функції:
// ініціалізація змінної лічильника ‘count’ в 0, якщо її немає в контексті
// вузла
var count = context.get('count')||0;
count += 1;
// збереження значення store в контексті вузла під іменем ‘count’
context.set('count',count);
// зробити це частиною вихідного об’єкту msg
msg.count = count;
return msg;
Node-RED може отримати або встановити декілька значень за один раз:
// Node-RED 0.19 або пізніше
var values = flow.get(["count", "colour", "temperature"]);
// values[0] це значення 'count'
// values[1] це значення 'colour'
// values[2] це значення 'temperature'
// Node-RED 0.19 або пізніше
flow.set(["count", "colour", "temperature"], [123, "red", "12.5"]);
У цьому випадку для будь-яких відсутніх значень буде встановлено значення null.
Якщо для контекстного сховища потрібен асинхронний доступ, то функції get
та set
вимагають додаткового параметра функції зворотного виклику.
// отримати одне значення
flow.get("count", function(err, myCount) { ... });
// отримати кілька значень
flow.get(["count", "colour"], function(err, count, colour) { ... })
// встановити одне значення
flow.set("count", 123, function(err) { ... })
// встановити кілька значень
flow.set(["count", "colour", [123, "red"], function(err) { ... })
Перший аргумент у функції зворотного виклику, err
, встановлюється лише тоді, коли при доступі до контексту виникла помилка.
Приклад асинхронної версії наведеної вище функції підрахунку кількості викликів функції:
context.get('count', function(err, count) {
if (err) {
node.error(err, msg);
} else {
// ініціалізація змінної лічильника ‘count’ в 0, якщо її
// немає в контексті вузла
count = count || 0;
count += 1;
// збереження значення store в контексті вузла під іменем ‘count’
context.set('count',count, function(err) {
if (err) {
node.error(err, msg);
} else {
// зробити це частиною вихідного об’єкту msg
msg.count = count;
// відправити повідомлення
node.send(msg);
}
});
}
});
З версії 0.19 можна налаштувати кілька контекстних сховищ. Наприклад, можуть бути використані два типа сховищ, що базуються на memory
і file
.
Функції роботи з контекстом get
/set
приймають необов'язковий параметр (у прикладі це storeName
) для ідентифікації сховища для використання.
// отримати значення синхронно - sync
var myCount = flow.get("count", storeName);
// отримати значення асинхронно - async
flow.get("count", storeName, function(err, myCount) { ... });
// встановити значення синхронно - sync
flow.set("count", 123, storeName);
// встановити значення асинхронно - async
flow.set("count", 123, storeName, function(err) { ... })
Коли Node-RED запускаються глобальний контекст може бути попередньо заповнений об'єктами. Це означено в основному файлі settings.js властивістю functionGlobalContext
.
Це може бути використано для Завантаження додаткових модулів в межах вузла Function.
Вузол Function також може забезпечити власне оформлення статусу таким же чином, як і інші вузли. Щоб встановити статус потрібно викликати node.status
Наприклад:
node.status({fill:"red", shape:"ring", text:"disconnected"});
node.status({fill:"green", shape:"dot", text:"connected"});
node.status({text:"Just text status"});
node.status({}); // для очищення статусу
Тоді будь-які оновлення статусу можуть також потрапляти до вузла Status.
У вузлі Function додаткові модулі не можуть бути завантажені безпосередньо. Вони повинні бути завантажені у вашому файлі settings.js і добавлені до властивості functionGlobalContext
. Наприклад, вбудований модуль os
може бути доступним для всіх функцій, додавши його до наступного файлу settings.js
functionGlobalContext: {
osModule:require('os')
}
після чого на модуль можна посилатися в межах функції як global.get('osModule')
.
Модулі, завантажені з вашого файлу налаштувань, повинні бути встановлені в тому ж каталозі, що і файл налаштувань. Для більшості користувачів цей каталог буде в користувацькій директорії за замовчуванням - ~/.node-red
:
cd ~/.node-red
npm install name_of_3rd_party_module
Since Node-RED 1.3.0
Якщо встановити functionExternalModules
на true
у вашому файлі settings.js, діалогове вікно редагування вузла Function надасть список, де ви можете додати додаткові модулі, які повинні бути доступні для вузла. Ви також вказуєте змінну, яка використовуватиметься для посилання на модуль у коді вузла.
Модулі автоматично встановлюються в ~/.node-red/externalModules/
, коли вузол розгортається.
Наступні об'єкти доступні в межах вузла Function.
node
-
node.id : ідентифікатор вузла Function - додано в 0,19
-
node.name : назва функціонального вузла - додано в 0,19
-
node.log(..) : запис повідомлення
-
node.warn(..) : log a warning message
-
node.error(..) : log an error message
-
node.debug(..) : log a debug message
-
node.trace(..) : log a trace message
-
node.on(..) : register an event handler
-
node.status(..) : update the node status
-
node.send(..) : send a message
context
-
context.get(..) : отримати властивість контексту вузлу
-
context.set(..) : встановити властивість контексту вузлу
-
context.keys(..) : повертає список всіх ключів властивостей контексту вузлу
-
context.flow : такий же як flow
-
context.global : такий же як global
flow
-
flow.get(..) : отримати властивість контексту потоку
-
flow.set(..) : встановити властивість контексту потоку
-
flow.keys(..) : повертає список усіх ключів властивостей контексту потоку
global
-
global.get(..) : отримати властивість глобального контекстну
-
global.set(..) : встановити властивість глобального контексту
-
global.keys(..) : повернути список усіх ключів властивостей глобального контексту
RED
RED.util.cloneMessage(..)
: безпечно клонує об'єкт повідомлення, щоб його можна було використовувати повторно
env
get(..)
: get an environment variable
Вузол Function також робить доступними наступні модулі та функції:
-
Buffer
- модуль Node.jsBuffer
-
console
- модуль Node.jsconsole
(node.log є наперед визначеним методом ведення журналу) -
util
- модуль Node.jsutil
-
setTimeout/clearTimeout
- функція тайм-ауту javascript. -
setInterval/clearInterval
- функції інтервалу javascript.
Вузол Function автоматично очищує будь-які прострочені тайм-аути або таймери інтервалів, коли він зупиняється або повторно розгортається.