async-task-current-limiter
介绍
异步任务限流器 async-task-current-limiter
为了解决Error: EMFILE, too many open files
的问题而生.
有太多的文件已打开,已经不能再打开了。
当您尝试一次在系统上打开太多文件时,就会发生这种情况。
由于 Node
的异步特性,如果您fs.readFile
快速连续执行很多或类似的操作,则可以轻松达到系统的 maxfiles
限制。
安装教程
yarn add https://github.com/masx200/async-task-current-limiter.git
使用说明
import AsyncLimiterClass from "@masx200/async-task-current-limiter";
示例
创建一个异步限流器对象,设定最大同时执行的异步任务数 30 个
当成函数使用
const asynclimiter = AsyncLimiterClass(30);
或者当成类使用
const asynclimiter = new AsyncLimiterClass(30);
监听异步限流器的free
和full
的事件
const listener = (data) => console.log(JSON.stringify(data));
asynclimiter.target.on("free", listener);
asynclimiter.target.on("full", listener);
用异步限流包装器,包装一个要限流的异步操作函数,
async function asyncread() {
return await new Promise((s) => {
setTimeout(() => {
s("data:" + Math.random());
}, Math.random() * 2000);
});
}
const limitread = asynclimiter.asyncwrap(asyncread);
进行大批量异步操作的限流
for (let i = 0; i < 1000; i++) {
setTimeout(() => {
limitread().then(console.log);
}, Math.random() * 5000);
}
原理
通过调用被限流的异步函数,将调用传入内部队列。如果活跃调用小于最大并发数,将会被取出直接执行,反之则继续呆在队列中。
当一个异步调用结束的时候,会从队列前取出调用执行。以此来保证异步调用的活跃量不高于限定值。
API
https://github.com/masx200/async-task-current-limiter/blob/master/dist/index.d.ts
AsyncLimiterClass(max)
1.当成函数使用
2.当成类使用
传入参数为限流器设定最大同时执行的任务数
asynclimiter.target
发布订阅的事件目标对象
https://github.com/masx200/event-emitter-target
事件 'free'
在限流器空闲的时候触发
可监听的参数类型为 statusdata
接口
事件 'full'
在限流器占满的时候触发
可监听的参数类型为 statusdata
接口
interface statusdata {
status: 空闲状态;
queue: {
max: number;
current: number;
};
limiter: {
max: number;
current: number;
};
}
asynclimiter.asyncwrap(fun)
异步限流包装器
传入函数必须返回一个Promise
,
返回一个被限流的异步操作函数
asynclimiter.status()
获取限流器状态的函数,返回'free'或者'full'
asynclimiter.limiter.max
异步限流器的最大同时执行的任务数
asynclimiter.limiter.current
异步限流器的当前同时执行的任务数
asynclimiter.queue.max
异步限流器的异步任务队列总数
asynclimiter.queue.current
异步限流器的异步任务队列中已经执行的任务个数
应用解决问题例子
使用异步限流器解决同时打开过多文件的报错
import fs from "fs";
import AsyncLimiterClass from "@masx200/async-task-current-limiter";
const asynclimiter = AsyncLimiterClass(50);
declare const files: string[];
const limitreadfile = asynclimiter.asyncwrap(fs.promises.readFile);
files.forEach(async (file) => {
const buf = await limitreadfile(file);
console.log(buf);
});