ES6 中将函数作为参数传入另一个异步函数 - aria2 RPC 的封装

ES6 中将函数作为参数传入另一个异步函数 - aria2 RPC 的封装

Anotia 65 2022-06-19

前段时间试图弄一个能远程控制 aria2 的脚本,并且能和其它平台(比如 Telegram)的交互方式整合起来。

我找到的 aria2 依赖对 Promise 的支持不太完整,部分只支持回调,并且没有对实时获取下载进度等方法做封装,因此得自己造一个轮子。

要实时追踪某个任务的进度,首先得记住任务的 GID,因此我使用了类来管理这些数据。aria2 库在新建完下载任务后会返回此任务的 GID,此时即可将 GID 记录下来。

记录好 GID 之后,便需要解决下载进度回显的问题。我想起 GramJS 的 downloadMedia 方法中有一个 progressCallback,可以传入自定义的回显函数来实现各种花里胡哨的进度条,遂打算 抄过来 学习一下。

ES6 中将函数作为参数传入另一个函数的大致方式:

async download(uri, progressCallback) {
    const gid = await this.aria2.call(...); // 创建任务,获取 gid
    
    return new Promise((resolve, reject) => {
        const interval = setInterval(() => {
            // 定时获取任务详情
            const progressInfo = await this.aria2.call(...);
            // 把任务信息通过 progressCallback 函数传回去
            progressCallback(...);
        }, 3 * 1000);
        // 将任务信息保存
        this.tasks[gid] = { resolve, interval, path: "..." };
        // 一小时后认为超时
        setTimeout(() => {
            delete this.tasks[gid];
            clearInterval(interval);
            reject(new Error("Timeout"));
        }, 3600 * 1000);
    }
}

这里并未用到 resolve 而是和 interval 一起先存进了 this.tasks。因为 aria2 何时会完成下载是不确定的,要等 aria2 收到 onDownloadComplete 事件我们才被动地知道任务完成了。所以在收到事件以及目标任务的 GID 后,我再去检查 this.tasks 中是否有这个任务,然后把它的 Promise resolve 掉。

this.aria2.on("onDownloadComplete", ([{ gid }]) => {
    // 如果存在这个任务
    if (this.tasks[gid]) {
        this.tasks[gid].resolve(path); // path 为下载好的文件路径,此处仅为示范
        clearInterval(this.tasks[gid].interval);
        delete this.tasks[gid];
    }
});

如此一来,既对 aria2 的下载文件功能做了异步封装,又支持了传入自己的 progress handler。调用的时候只需要一个:

const filePath = await aria2.download("https://example.com/some-file");

优雅,实在是太优雅了.jpg

(exception handling 不在本文讨论范围内)


# JavaScript