You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Async循环FTP批量下载触发MaxListenersExceededWarning如何解决?

问题描述

在Node.js 12.22环境下,使用async、ftp模块通过异步循环从FTP服务器批量获取文件,功能可正常运行,但控制台触发MaxListenersExceededWarning警告:

Possible EventEmitter memory leak detected. 11 error listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit

原代码

// ftp connexion managed previously
async.eachOfLimit(list, 1, function(e, i, ecb){

    ftp_client.get(e, function(err, stream){

        if(err || !stream)
        {
             return ecb();
        }
        else
        {
            stream.once('close', function() {
                console.log(" => OK");
                return ecb(null);
            });

            stream.once('error', function() {
                return ecb();
            });

            stream.pipe(fs.createWriteStream(mypath));
        }
    });


    ftp_client.on("error", function(err){

        return ecb();
    });
    
}, function(err){
   console.log("=> done");
   return callback();
});

控制台输出

=> OK
 => OK
 => OK
 => OK
 => OK
 => OK
 => OK
 => OK
 => OK
 => OK
 => OK
MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit
 => OK
 => OK
 => OK
...
修复方案

问题根源

每次循环都给同一个ftp_client实例添加新的error事件监听器,循环11次后超过了Node.js默认的10个监听器上限,触发警告。而且这些监听器不会自动移除,存在内存泄漏风险。

修复步骤

方案1:全局添加一次FTP错误监听器(推荐)

将ftp_client的error监听器移到循环外部,只添加一次即可,不需要每次循环重复绑定:

// ftp connexion managed previously
// 全局添加一次FTP客户端错误监听器,统一处理全局错误
ftp_client.on("error", function(err){
    console.error("FTP客户端全局错误:", err);
});

async.eachOfLimit(list, 1, function(e, i, ecb){

    ftp_client.get(e, function(err, stream){
        // 优先处理get方法返回的错误
        if(err || !stream)
        {
             return ecb(err); // 传递错误便于后续排查
        }
        
        stream.once('close', function() {
            console.log(" => OK");
            return ecb(null);
        });

        stream.once('error', function(streamErr) {
            console.error("文件流错误:", streamErr);
            return ecb(streamErr);
        });

        stream.pipe(fs.createWriteStream(mypath));
    });
    
}, function(err){
   console.log("=> done");
   if(err) console.error("批量下载结束,存在错误:", err);
   return callback(err);
});

方案2:循环内使用一次性监听器并手动清理

如果必须在每次循环的上下文里处理ftp_client的错误,可以用once绑定一次性监听器,触发后自动移除,或者手动清理:

// ftp connexion managed previously
async.eachOfLimit(list, 1, function(e, i, ecb){

    // 定义当前循环的错误处理函数
    function handleFtpError(err) {
        ecb(err);
    }
    // 绑定一次性监听器,触发后自动移除
    ftp_client.once("error", handleFtpError);

    ftp_client.get(e, function(err, stream){
        // 无论get是否成功,都手动移除当前循环的监听器
        ftp_client.off("error", handleFtpError);
        
        if(err || !stream)
        {
             return ecb(err);
        }
        
        stream.once('close', function() {
            console.log(" => OK");
            return ecb(null);
        });

        stream.once('error', function(streamErr) {
            console.error("文件流错误:", streamErr);
            return ecb(streamErr);
        });

        stream.pipe(fs.createWriteStream(mypath));
    });
    
}, function(err){
   console.log("=> done");
   return callback(err);
});

额外提示

不要盲目使用ftp_client.setMaxListeners(0)关闭监听器上限限制,这会掩盖内存泄漏的隐患,优先通过合理管理监听器数量解决问题。

内容的提问来源于stack exchange,提问作者Daphoque

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 19:25:32