上次教了Promise和async/await,今天來學更實用的技巧!

時間控制和模組化聽起來很高深,但其實就是「定時執行程式」和「把程式碼分檔案管理」。這兩個技巧學會了,你的Node.js程式會變得超專業!

時間控制篇:setTimeout 和 setInterval

setTimeout - 「等一下再執行」

想要程式等幾秒再執行某個動作?用setTimeout就對了!

// 基本用法:3秒後顯示歡迎訊息
const 顯示歡迎訊息 = () => {
    setTimeout(() => {
        console.log('歡迎使用我們的系統!');
    }, 3000); // 3000毫秒 = 3秒
};

顯示歡迎訊息();

實戰應用:API重試機制

網路不穩定時,API呼叫失敗很常見。用setTimeout做重試機制超實用:

// 聰明的API重試
const 呼叫API = async (網址, 重試次數 = 3) => {
    for (let i = 0; i < 重試次數; i++) {
        try {
            console.log(`第${i + 1}次嘗試呼叫API...`);
            
            // 模擬API呼叫(實際上用fetch)
            const 結果 = await 模擬API呼叫(網址);
            
            if (結果.成功) {
                console.log('API呼叫成功!');
                return 結果.資料;
            }
            
        } catch (錯誤) {
            console.log(`第${i + 1}次失敗:`, 錯誤.message);
            
            // 如果不是最後一次,就等一下再試
            if (i < 重試次數 - 1) {
                const 等待時間 = 1000 * (i + 1); // 1秒、2秒、3秒
                console.log(`等待${等待時間/1000}秒後重試...`);
                
                await new Promise(resolve => 
                    setTimeout(resolve, 等待時間)
                );
            }
        }
    }
    
    throw new Error('API呼叫失敗,已重試多次');
};

// 模擬API呼叫
const 模擬API呼叫 = async (網址) => {
    await new Promise(resolve => setTimeout(resolve, 500));
    
    // 30%機率成功
    if (Math.random() > 0.7) {
        return { 成功: true, 資料: { 訊息: 'API回應成功' } };
    } else {
        throw new Error('網路連接失敗');
    }
};

// 使用方式
呼叫API('https://api.example.com/data')
    .then(資料 => console.log('最終結果:', 資料))
    .catch(錯誤 => console.log('徹底失敗:', 錯誤.message));

setInterval - 「定期執行」

想要程式定期執行某個動作?setInterval就是你要的!

// 每30秒檢查一次系統狀態
const 開始系統監控 = () => {
    const 監控器 = setInterval(async () => {
        try {
            const 記憶體使用量 = process.memoryUsage();
            const 使用量MB = Math.round(記憶體使用量.used / 1024 / 1024);
            
            console.log('系統狀態檢查:', {
                時間: new Date().toLocaleString('zh-TW'),
                記憶體使用量: `${使用量MB}MB`,
                狀態: 使用量MB > 500 ? '警告!記憶體過高' : '正常'
            });
            
            // 如果記憶體用量過高,發警告
            if (使用量MB > 500) {
                console.log('🚨 記憶體使用量過高,請注意!');
            }
            
        } catch (錯誤) {
            console.log('監控出錯:', 錯誤.message);
        }
    }, 30000); // 每30秒執行一次
    
    // 回傳停止監控的函式
    return () => {
        clearInterval(監控器);
        console.log('監控已停止');
    };
};

// 啟動監控
const 停止監控 = 開始系統監控();

// 需要時可以停止
// 停止監控();

實用範例:自動清理快取

// 簡單的記憶體快取系統
const 快取 = new Map();

// 設定有過期時間的快取
const 設定快取 = (鍵, 值, 存活秒數 = 300) => {
    const 過期時間 = Date.now() + (存活秒數 * 1000);
    快取.set(鍵, { 值, 過期時間 });
    console.log(`快取設定:${鍵} (${存活秒數}秒後過期)`);
};

// 取得快取
const 取得快取 = (鍵) => {
    const 項目 = 快取.get(鍵);
    
    if (!項目) {
        return null;
    }
    
    if (Date.now() > 項目.過期時間) {
        快取.delete(鍵);
        return null;
    }
    
    return 項目.值;
};

// 自動清理過期的快取
const 開始快取清理 = () => {
    return setInterval(() => {
        const 現在 = Date.now();
        let 清理數量 = 0;
        
        for (const [鍵, 項目] of 快取.entries()) {
            if (現在 > 項目.過期時間) {
                快取.delete(鍵);
                清理數量++;
            }
        }
        
        if (清理數量 > 0) {
            console.log(`清理了${清理數量}個過期快取`);
        }
    }, 60000); // 每分鐘清理一次
};

// 使用範例
設定快取('用戶:123', { 姓名: '小明', 信箱: 'ming@test.com' }, 600);
設定快取('商品:456', { 名稱: 'iPhone', 價格: 30000 }, 300);

const 清理器 = 開始快取清理();

// 測試快取
setTimeout(() => {
    console.log('5秒後檢查快取:', 取得快取('用戶:123'));
}, 5000);

模組化篇:讓程式碼井然有序

為什麼要模組化?

想像你的程式碼是一個房子:

  • 不模組化:所有東西都塞在客廳,亂七八糟
  • 模組化:客廳、廚房、臥室分開,東西好找又整齊

CommonJS 模組系統(Node.js標準)

// utils/計算工具.js - 數學計算模組
const 加法 = (a, b) => a + b;
const 乘法 = (a, b) => a * b;

// 計算商品總價(含稅)
const 計算總價 = (價格, 數量, 稅率 = 0.05) => {
    const 小計 = 乘法(價格, 數量);
    const 稅額 = 小計 * 稅率;
    return 加法(小計, 稅額);
};

// 計算折扣後價格
const 計算折扣價 = (原價, 折扣百分比) => {
    return 原價 * (1 - 折扣百分比 / 100);
};

// 把函式匯出給其他檔案使用
module.exports = {
    加法,
    乘法,
    計算總價,
    計算折扣價
};
// utils/日期工具.js - 日期處理模組
const 格式化日期 = (日期, 格式 = 'YYYY-MM-DD') => {
    const d = new Date(日期);
    const 年 = d.getFullYear();
    const 月 = String(d.getMonth() + 1).padStart(2, '0');
    const 日 = String(d.getDate()).padStart(2, '0');
    
    return 格式
        .replace('YYYY', 年)
        .replace('MM', 月)
        .replace('DD', 日);
};

const 是否為週末 = (日期) => {
    const 星期 = new Date(日期).getDay();
    return 星期 === 0 || 星期 === 6; // 0是週日,6是週六
};

const 計算天數差 = (開始日期, 結束日期) => {
    const 開始 = new Date(開始日期);
    const 結束 = new Date(結束日期);
    const 差異毫秒 = 結束 - 開始;
    return Math.ceil(差異毫秒 / (1000 * 60 * 60 * 24));
};

module.exports = { 
    格式化日期, 
    是否為週末, 
    計算天數差 
};
// app.js - 主程式
const { 計算總價, 計算折扣價 } = require('./utils/計算工具');
const { 格式化日期, 是否為週末 } = require('./utils/日期工具');

// 使用計算工具
const 商品價格 = 1000;
const 數量 = 3;
const 總價 = 計算總價(商品價格, 數量, 0.08);
console.log(`商品總價(含8%稅):${總價}元`);

const 折扣價 = 計算折扣價(總價, 10); // 打9折
console.log(`折扣後價格:${折扣價}元`);

// 使用日期工具
const 今天 = 格式化日期(new Date());
console.log(`今天是:${今天}`);

if (是否為週末(new Date())) {
    console.log('今天是週末,商品打9折!');
}

實戰:建立一個用戶管理系統

// models/用戶.js - 用戶模型
class 用戶 {
    constructor(姓名, 信箱, 密碼) {
        this.id = Date.now() + Math.random(); // 簡單的ID生成
        this.姓名 = 姓名;
        this.信箱 = 信箱;
        this.密碼 = 密碼;
        this.註冊時間 = new Date();
        this.最後登入 = null;
    }
    
    取得基本資料() {
        return {
            id: this.id,
            姓名: this.姓名,
            信箱: this.信箱,
            註冊時間: this.註冊時間
        };
    }
    
    更新最後登入時間() {
        this.最後登入 = new Date();
    }
}

// 驗證信箱格式
const 驗證信箱 = (信箱) => {
    const 信箱格式 = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return 信箱格式.test(信箱);
};

module.exports = { 用戶, 驗證信箱 };
// services/用戶服務.js - 用戶業務邏輯
const { 用戶, 驗證信箱 } = require('../models/用戶');

const 用戶列表 = [];

const 建立用戶 = async (姓名, 信箱, 密碼) => {
    // 驗證輸入
    if (!姓名 || !信箱 || !密碼) {
        throw new Error('姓名、信箱、密碼都不能空白');
    }
    
    if (!驗證信箱(信箱)) {
        throw new Error('信箱格式不正確');
    }
    
    // 檢查信箱是否已存在
    const 現有用戶 = 用戶列表.find(用戶 => 用戶.信箱 === 信箱);
    if (現有用戶) {
        throw new Error('這個信箱已經註冊過了');
    }
    
    // 建立新用戶
    const 新用戶 = new 用戶(姓名, 信箱, 密碼);
    用戶列表.push(新用戶);
    
    console.log(`用戶 ${姓名} 註冊成功!`);
    return 新用戶.取得基本資料();
};

const 取得所有用戶 = () => {
    return 用戶列表.map(用戶 => 用戶.取得基本資料());
};

const 透過信箱找用戶 = (信箱) => {
    const 用戶 = 用戶列表.find(用戶 => 用戶.信箱 === 信箱);
    return 用戶 ? 用戶.取得基本資料() : null;
};

const 用戶登入 = (信箱, 密碼) => {
    const 用戶 = 用戶列表.find(u => u.信箱 === 信箱 && u.密碼 === 密碼);
    
    if (用戶) {
        用戶.更新最後登入時間();
        console.log(`${用戶.姓名} 登入成功`);
        return 用戶.取得基本資料();
    } else {
        throw new Error('信箱或密碼錯誤');
    }
};

module.exports = {
    建立用戶,
    取得所有用戶,
    透過信箱找用戶,
    用戶登入
};
// app.js - 主程式
const { 建立用戶, 取得所有用戶, 用戶登入 } = require('./services/用戶服務');

// 測試用戶系統();

把時間控制和模組化結合:實戰練習

現在我們把學到的技巧結合起來,做一個簡單但實用的系統監控工具:

// monitors/系統監控.js
const 監控記錄 = [];

const 記錄系統狀態 = () => {
    const 記憶體 = process.memoryUsage();
    const 狀態 = {
        時間: new Date().toLocaleString('zh-TW'),
        記憶體使用量MB: Math.round(記憶體.used / 1024 / 1024),
        CPU使用率: Math.random() * 100, // 模擬CPU使用率
        運行時間秒: Math.floor(process.uptime())
    };
    
    監控記錄.push(狀態);
    
    // 只保留最近50筆記錄
    if (監控記錄.length > 50) {
        監控記錄.shift();
    }
    
    return 狀態;
};

const 取得監控記錄 = () => 監控記錄;

const 檢查系統健康度 = () => {
    if (監控記錄.length === 0) return '無資料';
    
    const 最新狀態 = 監控記錄[監控記錄.length - 1];
    
    if (最新狀態.記憶體使用量MB > 500 || 最新狀態.CPU使用率 > 80) {
        return '警告';
    } else if (最新狀態.記憶體使用量MB > 300 || 最新狀態.CPU使用率 > 60) {
        return '注意';
    } else {
        return '正常';
    }
};

module.exports = {
    記錄系統狀態,
    取得監控記錄,
    檢查系統健康度
};
// services/監控服務.js
const { 記錄系統狀態, 檢查系統健康度 } = require('../monitors/系統監控');

let 監控定時器 = null;

const 開始監控 = (間隔秒數 = 30) => {
    if (監控定時器) {
        console.log('監控已經在運行中');
        return;
    }
    
    console.log(`開始系統監控,每${間隔秒數}秒檢查一次`);
    
    監控定時器 = setInterval(() => {
        const 狀態 = 記錄系統狀態();
        const 健康度 = 檢查系統健康度();
        
        console.log(`[${狀態.時間}] 記憶體: ${狀態.記憶體使用量MB}MB | CPU: ${狀態.CPU使用率.toFixed(1)}% | 健康度: ${健康度}`);
        
        // 如果系統不健康,發出警告
        if (健康度 === '警告') {
            console.log('🚨 系統資源使用量過高!');
        }
        
    }, 間隔秒數 * 1000);
};

const 停止監控 = () => {
    if (監控定時器) {
        clearInterval(監控定時器);
        監控定時器 = null;
        console.log('監控已停止');
    } else {
        console.log('監控並未運行');
    }
};

module.exports = {
    開始監控,
    停止監控
};
// app.js - 整合所有功能
const { 開始監控, 停止監控 } = require('./services/監控服務');
const { 建立用戶, 用戶登入 } = require('./services/用戶服務');

console.log('🚀 Node.js 系統啟動中...');

// 啟動系統監控
開始監控(10); // 每10秒監控一次

// 模擬一些用戶操作
const 模擬用戶操作 = async () => {
    try {
        await 建立用戶('測試用戶', 'test@example.com', 'password123');
        
        setTimeout(() => {
            用戶登入('test@example.com', 'password123');
        }, 5000);
        
    } catch (錯誤) {
        console.log('用戶操作錯誤:', 錯誤.message);
    }
};

模擬用戶操作();

// 30秒後停止監控(這只是示範)
setTimeout(() => {
    停止監控();
    console.log('系統關閉');
}, 30000);

推薦學習資源

學到這裡,你已經掌握了Node.js的核心技能!想要更進一步的話:

系統化學習 六角學院的Node.js課程會教你更完整的實戰技巧,包括Express框架、資料庫整合、API設計等等。課程是中文教學,很適合台灣的學習者。

下一步該怎麼做?

如果你是完全新手

  1. 把這兩篇文章的程式碼都跑過一遍
  2. 試著修改一些參數,看看會發生什麼
  3. 考慮上個系統化的課程

如果你已經有一些基礎

  1. 試著結合這些技巧做個小專案
  2. 學習Express框架和資料庫
  3. 開始建立自己的作品集

記住:程式設計最重要的就是多練習。不要只是看,一定要動手寫!

總結

今天學了Node.js的進階技巧:

  1. setTimeout:延遲執行,適合做重試機制
  2. setInterval:定期執行,適合做監控、清理
  3. 模組化:把程式碼分檔案管理,讓專案更好維護

這些技巧結合起來,你就可以做出很專業的Node.js應用程式了!

下次有機會再跟大家分享Express框架和資料庫的使用技巧。有問題的話歡迎留言討論!


*想要更完整的Node.js學習?立即查看六角學院課程,從基礎到實戰一次搞定!

創作者介紹
創作者 傑克淺談遊戲邏輯 的頭像
傑克的遊戲宇宙

傑克淺談遊戲邏輯

傑克的遊戲宇宙 發表在 痞客邦 留言(0) 人氣( 26 )