JSBox 异步编程详解:让你的脚本如丝般顺滑

JSBox 异步编程详解:让你的脚本如丝般顺滑

在 JSBox 开发中,异步编程是一个绕不开的核心概念。掌握异步编程,不仅能让你的脚本运行更加流畅,还能充分发挥 JSBox 的强大功能。让我们深入探讨 JSBox 中异步编程的方方面面。

一、为什么 JSBox 需要异步编程

1. 移动设备的特殊性

移动设备与桌面设备最大的区别在于资源的限制。iPhone 即使性能再强,也需要同时运行多个应用,保持系统流畅。如果你的 JSBox 脚本采用同步方式执行耗时操作,会导致整个界面"卡死",用户体验极差。

举个简单的例子:

// 错误示范:同步方式  
function downloadData() {  
    // 假设这个操作需要3秒  
    let data = $http.getSync("https://api.example.com/data");  
    // 这3秒内,整个界面都会卡住  
    $ui.alert(data);  
}  

2. JavaScript 的单线程特性

JavaScript 是单线程语言,这意味着同一时间只能执行一个任务。如果某个任务耗时较长,后续所有任务都必须等待。异步编程通过事件循环机制,让耗时操作在后台执行,不阻塞主线程。

3. 用户体验的要求

用户期望的是即时响应。点击按钮应该立即有反馈,滑动列表应该流畅无卡顿。异步编程确保了 UI 操作的优先级,让用户感受不到后台正在进行的复杂计算或网络请求。

二、JSBox 中的异步编程方式

1. 回调函数(Callback)

回调函数是最基础的异步编程方式。JSBox 的许多原生 API 都支持回调函数。

// 网络请求示例  
$http.get({  
    url: "https://api.github.com/users/github",  
    handler: function(resp) {  
        // 这是回调函数,请求完成后执行  
        let data = resp.data;  
        $ui.alert({  
            title: "用户信息",  
            message: `用户名: ${data.login}\n仓库数: ${data.public_repos}`  
        });  
    }  
});  
  
// 文件读取示例  
$file.read({  
    path: "data.json",  
    handler: function(data) {  
        if (data) {  
            let json = JSON.parse(data.string);  
            console.log("读取成功:", json);  
        } else {  
            console.log("文件不存在");  
        }  
    }  
});  

2. Promise:优雅的链式调用

Promise 解决了回调地狱的问题,让异步代码更加清晰。

// 将回调转换为 Promise  
function httpGet(url) {  
    return new Promise((resolve, reject) => {  
        $http.get({  
            url: url,  
            handler: function(resp) {  
                if (resp.error) {  
                    reject(resp.error);  
                } else {  
                    resolve(resp.data);  
                }  
            }  
        });  
    });  
}  
  
// 使用 Promise 链  
httpGet("https://api.github.com/users/github")  
    .then(userData => {  
        console.log("获取用户信息成功");  
        // 返回新的 Promise,获取用户的仓库  
        return httpGet(userData.repos_url);  
    })  
    .then(repoData => {  
        console.log(`用户有 ${repoData.length} 个仓库`);  
        // 获取第一个仓库的详情  
        if (repoData.length > 0) {  
            return httpGet(repoData[0].url);  
        }  
    })  
    .then(repoDetail => {  
        if (repoDetail) {  
            $ui.alert(`第一个仓库: ${repoDetail.name}`);  
        }  
    })  
    .catch(error => {  
        console.error("请求失败:", error);  
    });  

3. async/await:同步般的异步体验

async/await 让异步代码看起来像同步代码,极大提升了代码可读性。

// 定义异步函数  
async function getUserInfo(username) {  
    try {  
        // await 会等待 Promise 完成  
        let userData = await httpGet(`https://api.github.com/users/${username}`);  
        let repoData = await httpGet(userData.repos_url);  
          
        return {  
            name: userData.name,  
            company: userData.company,  
            repoCount: repoData.length,  
            totalStars: repoData.reduce((sum, repo) => sum + repo.stargazers_count, 0)  
        };  
    } catch (error) {  
        console.error("获取用户信息失败:", error);  
        return null;  
    }  
}  
  
// 使用异步函数  
async function main() {  
    // 显示加载提示  
    $ui.loading(true);  
      
    let info = await getUserInfo("torvalds");  
      
    $ui.loading(false);  
      
    if (info) {  
        $ui.alert({  
            title: info.name,  
            message: `公司: ${info.company}\n仓库数: ${info.repoCount}\n总星数: ${info.totalStars}`  
        });  
    }  
}  
  
main();  

4. 定时器和延迟执行

JSBox 提供了多种定时执行的方式。

// setTimeout - 延迟执行  
$delay(2, () => {  
    console.log("2秒后执行");  
});  
  
// setInterval - 重复执行  
let counter = 0;  
let timer = $timer.schedule({  
    interval: 1,  
    handler: function() {  
        counter++;  
        console.log(`计数: ${counter}`);  
          
        if (counter >= 10) {  
            timer.invalidate();  // 停止定时器  
        }  
    }  
});  
  
// 使用 Promise 实现延迟  
function delay(seconds) {  
    return new Promise(resolve => {  
        $delay(seconds, resolve);  
    });  
}  
  
// 在 async 函数中使用  
async function animatedAlert() {  
    $ui.alert("3");  
    await delay(1);  
    $ui.alert("2");  
    await delay(1);  
    $ui.alert("1");  
    await delay(1);  
    $ui.alert("开始!");  
}  

三、实际应用场景详解

场景1:批量图片下载器

这是一个典型的需要异步处理的场景:下载多张图片,显示进度,且不阻塞界面。

class ImageDownloader {  
    constructor(urls) {  
        this.urls = urls;  
        this.results = [];  
        this.progress = 0;  
    }  
      
    // 下载单张图片  
    async downloadImage(url, index) {  
        return new Promise((resolve, reject) => {  
            $http.download({  
                url: url,  
                progress: (bytesWritten, totalBytes) => {  
                    // 更新单个图片的下载进度  
                    let percentage = bytesWritten / totalBytes;  
                    this.updateProgress(index, percentage);  
                },  
                handler: (resp) => {  
                    if (resp.error) {  
                        reject(resp.error);  
                    } else {  
                        resolve(resp.data);  
                    }  
                }  
            });  
        });  
    }  
      
    // 更新进度显示  
    updateProgress(index, percentage) {  
        // 更新 UI 上的进度条  
        $("progress").value = (index + percentage) / this.urls.length;  
        $("progressLabel").text = `下载中: ${index + 1}/${this.urls.length}`;  
    }  
      
    // 并发下载所有图片  
    async downloadAll() {  
        // 创建所有下载任务  
        let tasks = this.urls.map((url, index) =>   
            this.downloadImage(url, index)  
                .then(data => {  
                    this.results[index] = data;  
                    return data;  
                })  
                .catch(error => {  
                    console.error(`图片 ${index} 下载失败:`, error);  
                    return null;  
                })  
        );  
          
        // 等待所有下载完成  
        await Promise.all(tasks);  
        return this.results.filter(data => data !== null);  
    }  
      
    // 限制并发数的下载  
    async downloadAllWithLimit(limit = 3) {  
        let results = [];  
        let executing = [];  
          
        for (let i = 0; i < this.urls.length; i++) {  
            const task = this.downloadImage(this.urls[i], i)  
                .then(data => {  
                    results[i] = data;  
                    return i;  
                });  
              
            executing.push(task);  
              
            if (executing.length >= limit) {  
                // 等待最快完成的一个  
                await Promise.race(executing);  
                executing = executing.filter(t => t !== task);  
            }  
        }  
          
        // 等待剩余的任务  
        await Promise.all(executing);  
        return results;  
    }  
}  
  
// 使用示例  
async function batchDownload() {  
    let urls = [  
        "https://example.com/image1.jpg",  
        "https://example.com/image2.jpg",  
        "https://example.com/image3.jpg",  
        // ... 更多URL  
    ];  
      
    // 创建界面  
    $ui.render({  
        props: {  
            title: "批量下载"  
        },  
        views: [{  
            type: "progress",  
            props: {  
                id: "progress",  
                value: 0  
            },  
            layout: (make, view) => {  
                make.left.right.inset(20);  
                make.centerY.equalTo(view.super);  
                make.height.equalTo(4);  
            }  
        }, {  
            type: "label",  
            props: {  
                id: "progressLabel",  
                text: "准备下载...",  
                align: $align.center  
            },  
            layout: (make, view) => {  
                make.centerX.equalTo(view.super);  
                make.bottom.equalTo($("progress").top).offset(-10);  
            }  
        }]  
    });  
      
    let downloader = new ImageDownloader(urls);  
    let images = await downloader.downloadAllWithLimit(3);  
      
    $ui.alert(`下载完成!成功: ${images.length}/${urls.length}`);  
}  

场景2:实时数据监控

监控多个数据源,实时更新显示,这需要精心设计的异步架构。

class DataMonitor {  
    constructor(sources) {  
        this.sources = sources;  // 数据源配置  
        this.data = {};         // 存储最新数据  
        this.intervals = [];    // 存储定时器  
        this.listeners = [];    // 数据更新监听器  
    }  
      
    // 添加数据更新监听器  
    addListener(callback) {  
        this.listeners.push(callback);  
    }  
      
    // 通知所有监听器  
    notifyListeners(source, data) {  
        this.listeners.forEach(callback => {  
            callback(source, data);  
        });  
    }  
      
    // 获取单个数据源  
    async fetchData(source) {  
        try {  
            let response = await httpGet(source.url);  
              
            // 数据处理  
            let processedData = source.processor ?   
                source.processor(response) : response;  
              
            // 更新缓存  
            this.data[source.name] = {  
                value: processedData,  
                timestamp: new Date(),  
                status: 'success'  
            };  
              
            // 通知监听器  
            this.notifyListeners(source.name, processedData);  
              
        } catch (error) {  
            this.data[source.name] = {  
                error: error.message,  
                timestamp: new Date(),  
                status: 'error'  
            };  
              
            this.notifyListeners(source.name, null);  
        }  
    }  
      
    // 启动监控  
    start() {  
        this.sources.forEach(source => {  
            // 立即获取一次  
            this.fetchData(source);  
              
            // 设置定时更新  
            let timer = $timer.schedule({  
                interval: source.interval || 5,  
                handler: () => {  
                    this.fetchData(source);  
                }  
            });  
              
            this.intervals.push(timer);  
        });  
    }  
      
    // 停止监控  
    stop() {  
        this.intervals.forEach(timer => timer.invalidate());  
        this.intervals = [];  
    }  
      
    // 获取所有数据的快照  
    getSnapshot() {  
        return JSON.parse(JSON.stringify(this.data));  
    }  
}  
  
// 使用示例  
function startMonitoring() {  
    // 配置数据源  
    let sources = [  
        {  
            name: "股票价格",  
            url: "https://api.example.com/stock/AAPL",  
            interval: 5,  
            processor: (data) => ({  
                price: data.price,  
                change: data.change,  
                changePercent: data.changePercent  
            })  
        },  
        {  
            name: "天气信息",  
            url: "https://api.example.com/weather",  
            interval: 300,  // 5分钟更新一次  
            processor: (data) => ({  
                temperature: data.main.temp,  
                description: data.weather[0].description  
            })  
        },  
        {  
            name: "系统状态",  
            url: "https://api.example.com/system/status",  
            interval: 10,  
            processor: (data) => ({  
                cpu: data.cpu_usage,  
                memory: data.memory_usage,  
                disk: data.disk_usage  
            })  
        }  
    ];  
      
    let monitor = new DataMonitor(sources);  
      
    // 添加数据更新处理  
    monitor.addListener((source, data) => {  
        if (data) {  
            updateUI(source, data);  
              
            // 检查告警条件  
            checkAlerts(source, data);  
        } else {  
            showError(source);  
        }  
    });  
      
    // 启动监控  
    monitor.start();  
      
    // 返回控制器供后续使用  
    return monitor;  
}  
  
function updateUI(source, data) {  
    // 更新界面显示  
    $(`${source}_value`).text = JSON.stringify(data, null, 2);  
    $(`${source}_status`).bgcolor = $color("green");  
}  
  
function checkAlerts(source, data) {  
    // 异步检查告警条件  
    $thread.background({  
        delay: 0,  
        handler: function() {  
            if (source === "股票价格" && data.changePercent < -5) {  
                $thread.main({  
                    handler: function() {  
                        $push.schedule({  
                            title: "股票预警",  
                            body: `股票跌幅超过5%: ${data.changePercent}%`,  
                            delay: 0  
                        });  
                    }  
                });  
            }  
        }  
    });  
}  

场景3:串行任务流程控制

有时候我们需要按照特定顺序执行一系列异步任务,每个任务依赖前一个任务的结果。

class TaskFlow {  
    constructor() {  
        this.tasks = [];  
        this.context = {};  // 任务间共享的上下文  
    }  
      
    // 添加任务  
    addTask(name, handler) {  
        this.tasks.push({  
            name: name,  
            handler: handler  
        });  
        return this;  // 支持链式调用  
    }  
      
    // 执行所有任务  
    async execute() {  
        console.log("开始执行任务流程");  
          
        for (let i = 0; i < this.tasks.length; i++) {  
            let task = this.tasks[i];  
            console.log(`执行任务 ${i + 1}/${this.tasks.length}: ${task.name}`);  
              
            try {  
                // 显示进度  
                $ui.loading({  
                    text: task.name  
                });  
                  
                // 执行任务,传入上下文  
                let result = await task.handler(this.context);  
                  
                // 保存结果到上下文  
                this.context[task.name] = result;  
                  
                console.log(`任务 ${task.name} 完成`);  
                  
            } catch (error) {  
                console.error(`任务 ${task.name} 失败:`, error);  
                $ui.loading(false);  
                  
                // 询问是否继续  
                let continueFlow = await this.askContinue(task.name, error);  
                if (!continueFlow) {  
                    throw new Error(`任务流程在 ${task.name} 中断`);  
                }  
            }  
        }  
          
        $ui.loading(false);  
        return this.context;  
    }  
      
    // 询问是否继续  
    askContinue(taskName, error) {  
        return new Promise((resolve) => {  
            $ui.alert({  
                title: "任务失败",  
                message: `任务 "${taskName}" 执行失败:\n${error.message}\n\n是否继续执行后续任务?`,  
                actions: [  
                    {  
                        title: "继续",  
                        handler: function() {  
                            resolve(true);  
                        }  
                    },  
                    {  
                        title: "中止",  
                        handler: function() {  
                            resolve(false);  
                        }  
                    }  
                ]  
            });  
        });  
    }  
}  
  
// 实际应用:发布文章到多个平台  
async function publishArticle() {  
    let flow = new TaskFlow();  
      
    flow  
        .addTask("读取文章", async (context) => {  
            // 从文件或剪贴板读取文章内容  
            let article = $clipboard.text;  
            if (!article) {  
                throw new Error("剪贴板中没有内容");  
            }  
              
            return {  
                title: article.split('\n')[0],  
                content: article  
            };  
        })  
        .addTask("处理图片", async (context) => {  
            // 查找文章中的图片,上传到图床  
            let imageUrls = [];  
            let matches = context.读取文章.content.match(/!\[.*?\]\((.*?)\)/g) || [];  
              
            for (let match of matches) {  
                let localPath = match.match(/\((.*?)\)/)[1];  
                if (localPath.startsWith("file://")) {  
                    // 上传本地图片  
                    let uploadedUrl = await uploadImage(localPath);  
                    imageUrls.push({  
                        local: localPath,  
                        remote: uploadedUrl  
                    });  
                }  
            }  
              
            return imageUrls;  
        })  
        .addTask("发布到平台A", async (context) => {  
            // 替换图片链接  
            let content = context.读取文章.content;  
            context.处理图片.forEach(img => {  
                content = content.replace(img.local, img.remote);  
            });  
              
            // 发布到平台A  
            let response = await httpPost("https://platform-a.com/api/publish", {  
                title: context.读取文章.title,  
                content: content  
            });  
              
            return response.articleId;  
        })  
        .addTask("发布到平台B", async (context) => {  
            // 平台B可能需要不同的格式  
            let formattedContent = convertToMarkdown(context.读取文章.content);  
              
            let response = await httpPost("https://platform-b.com/api/publish", {  
                title: context.读取文章.title,  
                body: formattedContent,  
                tags: ["JSBox", "技术"]  
            });  
              
            return response.url;  
        })  
        .addTask("生成报告", async (context) => {  
            // 生成发布报告  
            let report = `发布成功!\n\n`;  
            report += `平台A ID: ${context.发布到平台A}\n`;  
            report += `平台B URL: ${context.发布到平台B}\n`;  
            report += `上传图片数: ${context.处理图片.length}`;  
              
            // 保存报告  
            $file.write({  
                path: `publish_report_${Date.now()}.txt`,  
                data: $data({string: report})  
            });  
              
            return report;  
        });  
      
    try {  
        let result = await flow.execute();  
        $ui.alert({  
            title: "发布完成",  
            message: result.生成报告  
        });  
    } catch (error) {  
        $ui.alert({  
            title: "发布失败",  
            message: error.message  
        });  
    }  
}  

四、异步编程的常见陷阱与解决方案

1. 回调地狱

当多个异步操作需要按顺序执行时,使用回调函数会导致代码嵌套过深。

// 错误示范:回调地狱  
getData(function(a) {  
    getMoreData(a, function(b) {  
        getMoreData(b, function(c) {  
            getMoreData(c, function(d) {  
                getMoreData(d, function(e) {  
                    // 嵌套太深,难以维护  
                });  
            });  
        });  
    });  
});  
  
// 解决方案:使用 async/await  
async function getAllData() {  
    let a = await getData();  
    let b = await getMoreData(a);  
    let c = await getMoreData(b);  
    let d = await getMoreData(c);  
    let e = await getMoreData(d);  
    return e;  
}  

2. 错误处理不当

异步操作的错误处理容易被忽略,导致程序出现未捕获的异常。

// 错误示范:没有错误处理  
async function riskyOperation() {  
    let data = await fetchData();  // 如果失败会抛出异常  
    return processData(data);  
}  
  
// 正确做法:完善的错误处理  
async function safeOperation() {  
    try {  
        let data = await fetchData();  
        return processData(data);  
    } catch (error) {  
        console.error("操作失败:", error);  
          
        // 可以选择:  
        // 1. 返回默认值  
        return getDefaultData();  
          
        // 2. 重新抛出特定错误  
        throw new Error(`数据处理失败: ${error.message}`);  
          
        // 3. 记录错误并通知用户  
        logError(error);  
        $ui.alert("操作失败,请稍后重试");  
    }  
}  

3. 并发控制不当

同时发起太多异步操作可能导致资源耗尽或请求被限制。

// 错误示范:无限制并发  
async function downloadAllImages(urls) {  
    // 同时下载所有图片,可能导致内存溢出或请求被拒绝  
    let promises = urls.map(url => downloadImage(url));  
    return Promise.all(promises);  
}  
  
// 解决方案:并发数量控制  
async function downloadWithConcurrencyLimit(urls, limit = 5) {  
    let results = [];  
    let executing = [];  
      
    for (const [index, url] of urls.entries()) {  
        const promise = downloadImage(url).then(result => {  
            results[index] = result;  
            return index;  
        });  
          
        executing.push(promise);  
          
        if (executing.length >= limit) {  
            // 等待最快的一个完成  
            const completed = await Promise.race(executing);  
            executing = executing.filter(p => p !== promise);  
        }  
    }  
      
    // 等待所有剩余的  
    await Promise.all(executing);  
    return results;  
}  
  
// 使用第三方库的方案  
class PromisePool {  
    constructor(concurrency) {  
        this.concurrency = concurrency;  
        this.current = 0;  
        this.queue = [];  
    }  
      
    async run(task) {  
        while (this.current >= this.concurrency) {  
            await new Promise(resolve => this.queue.push(resolve));  
        }  
          
        this.current++;  
          
        try {  
            return await task();  
        } finally {  
            this.current--;  
            if (this.queue.length > 0) {  
                const resolve = this.queue.shift();  
                resolve();  
            }  
        }  
    }  
}  

4. 内存泄漏

长时间运行的异步操作如果不正确清理,会导致内存泄漏。

class ResourceManager {  
    constructor() {  
        this.timers = new Set();  
        this.requests = new Map();  
    }  
      
    // 创建可取消的定时器  
    createTimer(callback, interval) {  
        let timer = $timer.schedule({  
            interval: interval,  
            handler: callback  
        });  
          
        this.timers.add(timer);  
          
        return {  
            cancel: () => {  
                timer.invalidate();  
                this.timers.delete(timer);  
            }  
        };  
    }  
      
    // 创建可取消的HTTP请求  
    async createRequest(url, options = {}) {  
        let cancelled = false;  
        let requestId = Date.now();  
          
        this.requests.set(requestId, {  
            cancelled: false  
        });  
          
        try {  
            let result = await httpGet(url);  
              
            if (this.requests.get(requestId).cancelled) {  
                throw new Error("Request cancelled");  
            }  
              
            return result;  
        } finally {  
            this.requests.delete(requestId);  
        }  
    }  
      
    // 清理所有资源  
    cleanup() {  
        // 取消所有定时器  
        this.timers.forEach(timer => timer.invalidate());  
        this.timers.clear();  
          
        // 标记所有请求为已取消  
        this.requests.forEach(request => request.cancelled = true);  
        this.requests.clear();  
    }  
}  
  
// 使用示例  
let manager = new ResourceManager();  
  
// 页面退出时清理  
$app.listen({  
    exit: function() {  
        manager.cleanup();  
    }  
});  

五、高级异步模式

1. 事件驱动架构

创建一个完整的事件系统,实现组件间的解耦通信。

class EventEmitter {  
    constructor() {  
        this.events = {};  
    }  
      
    on(event, callback) {  
        if (!this.events[event]) {  
            this.events[event] = [];  
        }  
        this.events[event].push(callback);  
          
        // 返回取消订阅的函数  
        return () => {  
            this.events[event] = this.events[event].filter(cb => cb !== callback);  
        };  
    }  
      
    once(event, callback) {  
        const unsubscribe = this.on(event, (...args) => {  
            callback(...args);  
            unsubscribe();  
        });  
    }  
      
    emit(event, ...args) {  
        if (!this.events[event]) return;  
          
        // 异步执行所有回调  
        this.events[event].forEach(callback => {  
            Promise.resolve().then(() => callback(...args));  
        });  
    }  
      
    async emitAsync(event, ...args) {  
        if (!this.events[event]) return;  
          
        // 顺序执行所有异步回调  
        for (const callback of this.events[event]) {  
            await callback(...args);  
        }  
    }  
}  
  
// 应用示例:数据同步系统  
class DataSyncSystem extends EventEmitter {  
    constructor() {  
        super();  
        this.setupEventHandlers();  
    }  
      
    setupEventHandlers() {  
        // 数据变更时自动同步  
        this.on('dataChanged', async (data) => {  
            await this.syncToCloud(data);  
        });  
          
        // 同步成功后更新UI  
        this.on('syncSuccess', (data) => {  
            this.updateUI(data);  
        });  
          
        // 错误处理  
        this.on('error', (error) => {  
            this.handleError(error);  
        });  
    }  
      
    async updateData(newData) {  
        try {  
            // 更新本地数据  
            await this.saveLocal(newData);  
              
            // 触发数据变更事件  
            this.emit('dataChanged', newData);  
              
        } catch (error) {  
            this.emit('error', error);  
        }  
    }  
      
    async syncToCloud(data) {  
        try {  
            let result = await uploadToCloud(data);  
            this.emit('syncSuccess', result);  
        } catch (error) {  
            this.emit('syncError', error);  
        }  
    }  
}  

2. 响应式编程模式

实现类似 RxJS 的观察者模式,处理异步数据流。

class Observable {  
    constructor(subscriber) {  
        this.subscriber = subscriber;  
    }  
      
    subscribe(observer) {  
        // 标准化观察者对象  
        const normalizedObserver = {  
            next: observer.next || (() => {}),  
            error: observer.error || (() => {}),  
            complete: observer.complete || (() => {})  
        };  
          
        // 执行订阅逻辑  
        const unsubscribe = this.subscriber(normalizedObserver);  
          
        return {  
            unsubscribe: unsubscribe || (() => {})  
        };  
    }  
      
    // 操作符:map  
    map(transform) {  
        return new Observable(observer => {  
            return this.subscribe({  
                next: value => observer.next(transform(value)),  
                error: err => observer.error(err),  
                complete: () => observer.complete()  
            });  
        });  
    }  
      
    // 操作符:filter  
    filter(predicate) {  
        return new Observable(observer => {  
            return this.subscribe({  
                next: value => {  
                    if (predicate(value)) {  
                        observer.next(value);  
                    }  
                },  
                error: err => observer.error(err),  
                complete: () => observer.complete()  
            });  
        });  
    }  
      
    // 操作符:debounce  
    debounce(delay) {  
        return new Observable(observer => {  
            let timeoutId = null;  
              
            const subscription = this.subscribe({  
                next: value => {  
                    if (timeoutId) {  
                        clearTimeout(timeoutId);  
                    }  
                      
                    timeoutId = setTimeout(() => {  
                        observer.next(value);  
                    }, delay);  
                },  
                error: err => observer.error(err),  
                complete: () => {  
                    if (timeoutId) {  
                        clearTimeout(timeoutId);  
                    }  
                    observer.complete();  
                }  
            });  
              
            return () => {  
                if (timeoutId) {  
                    clearTimeout(timeoutId);  
                }  
                subscription.unsubscribe();  
            };  
        });  
    }  
}  
  
// 创建自定义Observable  
function fromEvent(element, eventName) {  
    return new Observable(observer => {  
        const handler = event => observer.next(event);  
          
        element.addEventListener(eventName, handler);  
          
        return () => {  
            element.removeEventListener(eventName, handler);  
        };  
    });  
}  
  
// 使用示例:搜索框自动完成  
function setupAutoComplete() {  
    const searchInput = $("searchInput");  
      
    const searchStream = fromEvent(searchInput, 'change')  
        .map(event => event.target.text)  
        .filter(text => text.length > 2)  
        .debounce(300);  
      
    const subscription = searchStream.subscribe({  
        next: async (searchTerm) => {  
            $ui.loading(true);  
              
            try {  
                const results = await searchAPI(searchTerm);  
                displayResults(results);  
            } catch (error) {  
                showError(error);  
            } finally {  
                $ui.loading(false);  
            }  
        },  
        error: error => console.error("搜索错误:", error)  
    });  
      
    // 清理  
    return () => subscription.unsubscribe();  
}  

六、最佳实践总结

1. 选择合适的异步模式

  • 简单操作:使用回调函数
  • 链式操作:使用 Promise
  • 复杂流程:使用 async/await
  • 事件处理:使用事件发射器
  • 数据流:使用响应式编程

2. 错误处理原则

// 统一的错误处理器  
class ErrorHandler {  
    static async handle(operation, options = {}) {  
        const {  
            retries = 3,  
            retryDelay = 1000,  
            fallback = null,  
            onError = null  
        } = options;  
          
        for (let i = 0; i < retries; i++) {  
            try {  
                return await operation();  
            } catch (error) {  
                console.error(`尝试 ${i + 1}/${retries} 失败:`, error);  
                  
                if (onError) {  
                    onError(error, i + 1);  
                }  
                  
                if (i < retries - 1) {  
                    await delay(retryDelay * (i + 1));  
                } else if (fallback !== null) {  
                    return typeof fallback === 'function' ? fallback() : fallback;  
                } else {  
                    throw error;  
                }  
            }  
        }  
    }  
}  
  
// 使用示例  
const data = await ErrorHandler.handle(  
    () => fetchDataFromAPI(),  
    {  
        retries: 3,  
        retryDelay: 2000,  
        fallback: () => getDataFromCache(),  
        onError: (error, attempt) => {  
            console.log(`第 ${attempt} 次尝试失败`);  
        }  
    }  
);  

3. 性能优化技巧

// 缓存异步结果  
class AsyncCache {  
    constructor(ttl = 60000) {  // 默认缓存60秒  
        this.cache = new Map();  
        this.ttl = ttl;  
    }  
      
    async get(key, fetcher) {  
        const cached = this.cache.get(key);  
          
        if (cached && Date.now() - cached.timestamp < this.ttl) {  
            return cached.value;  
        }  
          
        const value = await fetcher();  
          
        this.cache.set(key, {  
            value: value,  
            timestamp: Date.now()  
        });  
          
        return value;  
    }  
      
    clear() {  
        this.cache.clear();  
    }  
}  
  
// 批量请求优化  
class BatchProcessor {  
    constructor(processor, batchSize = 10, delay = 100) {  
        this.processor = processor;  
        this.batchSize = batchSize;  
        this.delay = delay;  
        this.queue = [];  
        this.processing = false;  
    }  
      
    async add(item) {  
        return new Promise((resolve, reject) => {  
            this.queue.push({ item, resolve, reject });  
              
            if (!this.processing) {  
                this.startProcessing();  
            }  
        });  
    }  
      
    async startProcessing() {  
        this.processing = true;  
          
        while (this.queue.length > 0) {  
            const batch = this.queue.splice(0, this.batchSize);  
              
            try {  
                const results = await this.processor(batch.map(b => b.item));  
                  
                batch.forEach((b, index) => {  
                    b.resolve(results[index]);  
                });  
            } catch (error) {  
                batch.forEach(b => b.reject(error));  
            }  
              
            if (this.queue.length > 0) {  
                await delay(this.delay);  
            }  
        }  
          
        this.processing = false;  
    }  
}  

结语

异步编程是 JSBox 开发中的核心技能,掌握好异步编程不仅能让你的脚本运行更流畅,还能实现更复杂、更强大的功能。从简单的回调函数到复杂的响应式编程,每种模式都有其适用场景。

关键是要理解异步的本质——不阻塞主线程,让用户界面始终保持响应。在实际开发中,要根据具体需求选择合适的异步模式,做好错误处理,注意性能优化,避免常见陷阱。

随着实践的深入,你会发现异步编程不再是障碍,而是打开新世界大门的钥匙。它让你能够优雅地处理网络请求、文件操作、定时任务等各种场景,创造出真正专业级的 JSBox 应用。