Khai báo biến
JavaScript có tới 3 từ khóa để khai báo biến: const, let, và var. Mỗi từ khóa có mục đích và quy tắc riêng
// 1. Dùng const vì thư viện nạp vào không bao giờ thay đổi
const express = require('express');
// 2. Dùng const vì cấu hình kết nối cố định
const PORT = 3000;
// 3. Dùng let vì số lượng request sẽ tăng lên liên tục mỗi khi có người truy cập
let requestCount = 0;
requestCount = requestCount + 1; // Hợp lệ vì dùng let
Khai báo hàm
Trong JavaScript, có 3 cách phổ biến nhất để khai báo một hàm (function)
Hàm thông thường (Function Declaration)
Đây là cách truyền thống và phổ biến nhất. Có cơ chế Hoisting (được đưa lên đầu file khi chạy), nghĩa là bạn có thể gọi hàm ở trước dòng khai báo hàm đó vẫn được.
// Khai báo hàm
function tinhTong(a, b) {
return a + b;
}
// Gọi hàm
let ketQua = tinhTong(5, 10); // Kết quả: 15
Hàm biểu thức (Function Expression)
Cách này gắn một hàm không tên (anonymous function) vào một biến. Không có tính năng Hoisting (phải khai báo trước rồi mới được gọi sau).
// Khai báo hàm gán vào biến
const tinhTich = function(a, b) {
return a * b;
};
Hàm mũi tên (Arrow Function - Chuẩn ES6 hiện đại)
Đây là cú pháp ngắn gọn và rất được ưa chuộng trong JavaScript hiện đại (Đặc biệt là khi làm việc với ReactJS hoặc Node.js).
Đặc điểm: Cú pháp cực kỳ gọn, không có từ khóa function mà dùng dấu mũi tên =>. Nếu hàm chỉ có 1 dòng lệnh trả về giá trị, bạn có thể bỏ luôn dấu ngoặc nhọn {} và từ khóa return
// Khai báo Arrow Function đầy đủ
const chaoMuiTen = (ten) => {
return "Xin chào " + ten;
};
// Hoặc viết rút gọn (nếu chỉ có 1 dòng)
const nhanDoi = x => x * 2;
// Gọi hàm
console.log(nhanDoi(5)); // Kết quả: 10
// Gọi hàm let ketQua = tinhTich(4, 5); // Kết quả: 20
Dấu => trong JavaScript được gọi là Toán tử mũi tên (Arrow operator) hoặc dùng trong cú pháp Arrow Function (Hàm mũi tên).
Đừng nhầm lẫn nó với các toán tử so sánh (như >= là "lớn hơn hoặc bằng"). => là một cú pháp hoàn toàn mới được giới thiệu từ bản cập nhật ES6 nhằm giúp việc viết hàm trở nên ngắn gọn và hiện đại hơn.
Dưới đây là cách hiểu và cách dùng chi tiết:
Ý nghĩa của => Nó giống như một mũi tên chỉ hướng: "Đầu vào bên trái sẽ thực hiện hành động hoặc trả về kết quả ở bên phía phải".
Hàm bất đồng bộ
Bất cứ khi nào bạn viết code phải chờ đợi phản hồi từ bên ngoài (gọi mạng Internet, đọc/ghi file ổ cứng, truy vấn database,...) bạn đều cần dùng hàm async kết hợp với await.
// Khai báo hàm async thông thường
async function layThongTinNguoiDung() {
// Giả lập việc gọi API lấy dữ liệu mất 2 giây
let ketQua = await fetch('https://api.example.com/user');
let duLieu = await ketQua.json();
return duLieu;
}
// Kết hợp async với Arrow Function (=>)
// Đặt async ở trước ngoặc chứa tham số
const ketNoiTuya = async (deviceId) => {
try {
// Chờ kết nối thiết bị Tuya hoàn tất rồi mới chạy tiếp
const status = await tuya.getDeviceStatus(deviceId);
console.log(status);
} catch (error) {
console.log("Lỗi kết nối:", error);
}
};
Khai báo thư viện
Trong JavaScript (đặc biệt là môi trường Node.js và ES6+ hiện đại), có 2 hệ thống quản lý module chính để khai báo và sử dụng thư viện:
Hệ thống CommonJS
Cách gọi thư viện
const express = require('express');
const mongoose = require('mongoose');
Cách xuất (export) code của bạn để file khác dùng
module.exports = { myFunction };
// hoặc
module.exports = myFunction;
- const: Là từ khóa trong JavaScript
- express: Là tên biến -
- require('express'): Là hàm cốt lõi trong Node.js
Hệ thống ES Modules (ESM - Hiện đại)
Cách gọi thư viện: Sử dụng từ khóa import và from.
import express from 'express';
// Nhập một hàm cụ thể từ thư viện chuẩn
import { readFile } from 'fs';
// Đọc nội dung file 'data.txt' theo chuẩn bất đồng bộ (asynchronous)
readFile('data.txt', 'utf8', (err, data) => {
if (err) {
console.error('Lỗi khi đọc file:', err);
return;
}
console.log('Nội dung file:', data);
});
// readFile là một hàm có sẵn trong module fs của Node.js.
// Vì nó là hàm, bạn có thể gọi trực tiếp nó hoặc truyền tham số vào.
import { TuyaContext } from '@tuya/tuya-connector-nodejs';
// Khởi tạo TuyaContext với thông tin xác thực
const tuya = new TuyaContext({
baseUrl: 'https://openapi.tuyaus.com',
accessKey: 'xxx',
secretKey: 'xxx',
});
// TuyaContext là một Class (Lớp đối tượng) nên new để khởi tạo ra một đối tượng cụ thể
Dấu {} trong trường hợp này là cú pháp destructuring (bóc tách) của ES Modules để lấy ra một thành phần cụ thể từ thư viện. Tùy thuộc vào thư viện đó là gì mà thành phần bên trong {} có thể là một hàm, một lớp (class), hoặc một biến.
Dấu { ... } chỉ đơn giản là cái túi để bạn chọn lấy đúng món đồ mình cần từ kho của thư viện. Món đồ lấy ra có thể là hàm (để thực thi một hành động như đọc file) hoặc là class (để tạo ra một đối tượng quản lý như TuyaContext).
Cách xuất (export) code của bạn:
export function myFunction() { ... }
// hoặc xuất mặc định
export default myFunction;
Chạy theo chu kì bên server
Sử dụng setInterval để gọi hàm lấy trạng thái theo thời gian định trước
// Hàm gọi API lấy trạng thái
const getDeviceStatus = async () => {
// ...
};
// Sử dụng setInterval: Cứ mỗi 5000 mili-giây (5 giây) sẽ chạy lại hàm getDeviceStatus 1 lần
const THOI_GIAN_LAP = 5000;
setInterval(getDeviceStatus, THOI_GIAN_LAP);
console.log('Đã thiết lập tự động lấy trạng thái thiết bị mỗi 5 giây...');
Áp dụng kỹ thuật Recursive `setTimeout để thu thập dữ liệu định kỳ từ thiết bị Tuya mà không sợ bị chồng chéo lệnh
// 3. Xây dựng hàm Recursive setTimeout (Vòng lặp thông minh)
const startTuyaCollector = () => {
const loop = async () => { // Xem loop là 1 hàm
try {
// Bước A: Chạy hàm lấy dữ liệu và CHỜ cho đến khi xong xuôi hoàn toàn
await collectTuyaMetrics();
} catch (error) {
// Bắt lỗi nếu mạng rớt hoặc API lỗi để chương trình không bị crash
console.error('Lỗi trong quá trình thu thập:', error.message);
} finally {
// Bước B: Sau khi lần chạy trước đã HOÀN TẤT (dù thành công hay thất bại),
// lúc này mới đếm ngược 5 giây rồi gọi lại chính hàm loop này.
setTimeout(loop, INTERVAL_TIME);
}
};
// Kích hoạt chu kỳ lần đầu tiên
loop();
};
Cập nhật trang thái dùng API
Kỹ thuật API Endpoint + Request từ Front-end (On-demand / Chủ động theo yêu cầu)
// Tạo một biến lưu trạng thái hiện tại trên server
let latestStatus = null;
// Mỗi khi interval lấy được dữ liệu, cập nhật vào biến này
// Tạo API cho Front-end gọi vào
app.get('/api/status', (req, res) => {
res.json({ status: latestStatus });
});
Code mẫu Express Server với 4 phương thức HTTP
Dưới đây là một ví dụ hoàn chỉnh bằng Express framework (sử dụng chuẩn ES Module import) mô tả cấu trúc của 4 phương thức HTTP: GET, POST, PUT, và DELETE trong một ứng dụng quản lý thiết bị IoT.
import express from 'express';
const app = express();
const PORT = 3001;
// Middleware quan trọng: Giúp server đọc được dữ liệu dạng JSON gửi lên từ Front-end (POST, PUT)
app.use(express.json());
// ==========================================
// 1. GET: Lấy dữ liệu (Ví dụ: Xem trạng thái thiết bị)
// ==========================================
app.get('/api/devices/:id', (req, res) => {
// Lấy tham số truyền trên URL (ví dụ: /api/devices/123 -> id = "123")
const deviceId = req.params.id;
// Xử lý logic lấy trạng thái từ Tuya...
res.json({
success: true,
message: `Đã lấy thành công trạng thái của thiết bị: ${deviceId}`,
status: { switch_1: true }
});
});
// ==========================================
// 2. POST: Gửi lệnh hoặc tạo mới (Ví dụ: Bật/tắt thiết bị)
// ==========================================
app.post('/api/devices/control', (req, res) => {
// Lấy dữ liệu người dùng gửi từ Front-end xuống (nằm ở req.body)
const { deviceId, command } = req.body;
// Xử lý gửi lệnh sang Tuya Cloud...
console.log(`Đang thực thi lệnh ${command} cho thiết bị ${deviceId}`);
res.json({
success: true,
message: `Đã gửi lệnh thành công tới thiết bị ${deviceId}`
});
});
// ==========================================
// 3. PUT: Cập nhật thông tin (Ví dụ: Đổi tên hoặc cấu hình thiết bị)
// ==========================================
app.put('/api/devices/:id', (req, res) => {
const deviceId = req.params.id;
const newConfig = req.body; // Dữ liệu mới cần cập nhật
// Xử lý cập nhật trong Database hoặc cấu hình...
res.json({
success: true,
message: `Đã cập nhật cấu hình cho thiết bị ${deviceId}`,
data: newConfig
});
});
// ==========================================
// 4. DELETE: Xóa dữ liệu (Ví dụ: Xóa thiết bị khỏi hệ thống)
// ==========================================
app.delete('/api/devices/:id', (req, res) => {
const deviceId = req.params.id;
// Xử lý xóa thiết bị...
res.json({
success: true,
message: `Đã xóa thiết bị ${deviceId} khỏi danh sách quản lý`
});
});
// Khởi động server
app.listen(PORT, () => {
console.log(`Server đang chạy tại cổng ${PORT}`);
});
Các bước tạo dự án có server
- Bước 1: chạy
npm initkhai báo các thông tin để tạo ra filepackage.json - Bước 2 cài thư viện, sau bước này thư viện được add vào folder
node_modules- Ví dụ thư viên
tuyapidùng lệnhnpm install tuyapi
- Ví dụ thư viên
- Bước 3 tạo file server.js
- Bước 4 chạy file server.js
- Đảm bảo máy tính/server có cài node.js
- Dùng lệnh
cdtrỏ đến thư mục có fileserver.js. Ví dụ:node js/server.js
- Bước 5 tìm local key
- Khi cài
tuyapisẽ có góituya-cligiúp tìm key và quét thiết bị trong LAN npm install --global @tuyapi/cli: cài đặt công cụ dòng lệnh của họ trên toàn hệ thống để quét thiết bịtuya-cli wizard: để kết nối với tài khoản Tuya Cloud hoặc quét mạng LAN
- Khi cài
- Bước 6 cài
tinytuya- Cài python xe trên mạng, kiểm tra qua lệnh
python --version pip install tinytuya: cài đặt tinytuyapython -m tinytuya wizard: để quét mạng LAN tìm thiết bị thông minh và lấy Local Key
- Cài python xe trên mạng, kiểm tra qua lệnh