学习笔记:zhuanlan.zhihu.com/p/549829103
juejin.cn/post/715877…
使用案例
import delay from 'delay';
import timeSpan from 'time-span';
const end = timeSpan()
const fetchData = async () => {
await delay(1000) // 延时1s
return 10
}
const fetchData1 = async() => {
await delay(2000) // 延时2s
return 20
}
const fetchData2 = async () => {
await delay(3000) // 延时3s
return 30
}
const input = [
fetchData(),
fetchData1(),
fetchData2()
];
const result = await Promise.all(input)
console.log(result); // [ 10, 20, 30 ]
console.log(end()) // 3011.592865 运行的毫秒数
使用p-limit来限制一下并发数
import pLimit from 'p-limit';
import delay from 'delay';
import timeSpan from 'time-span';
const limit = pLimit(1);
const end = timeSpan()
const input = [
limit(() => fetchData()),
limit(() => fetchData1()),
limit(() => fetchData2())
];
const result = await Promise.all(input)
console.log(result); // [ 10, 20, 30 ]
console.log(end()) // 6012.97549 运行的毫秒数
限制并发
并发
代码的执行一般是按顺序执行的,所以同步任务是不存在并发的。但如果是异步任务的话,即使有先后顺序,它的执行的结果也是不受控制的。比如Promise.race()最终返回resolve的Promise是不确定的。
控制并发请求数-控制在并发数任务在pending的过程中,不再继续发送请求
代码
import Queue from 'yocto-queue';
export default function pLimit(concurrency) {
//concurrency是不是整数且大于1
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
throw new TypeError('Expected `concurrency` to be a number from 1 and up');
}
const queue = new Queue();
let activeCount = 0;
const next = () => {
activeCount--;
if (queue.size > 0) {
queue.dequeue()();
}
};
const run = async (fn, resolve, args) => {
activeCount++;
const result = (async () => fn(...args))();
resolve(result);
try {
await result;
} catch {}
next();
};
const enqueue = (fn, resolve, args) => {
queue.enqueue(run.bind(undefined, fn, resolve, args));
(async () => {
// This function needs to wait until the next microtask before comparing
// `activeCount` to `concurrency`, because `activeCount` is updated asynchronously
// when the run function is dequeued and called. The comparison in the if-statement
// needs to happen asynchronously as well to get an up-to-date value for `activeCount`.
await Promise.resolve();
if (activeCount < concurrency && queue.size > 0) {
queue.dequeue()();
}
})();
};
//接收一个并发数,并返回一个Promise函数
const generator = (fn, ...args) => new Promise(resolve => {
enqueue(fn, resolve, args);
});
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount,
},
pendingCount: {
get: () => queue.size,
},
clearQueue: {
value: () => {
queue.clear();
},
},
});
return generator;
}
generator
export default function pLimit(concurrency) {
//接收异步函数和剩余参数,调用Promise将resolve传递给enqueue函数。
const generator = (fn, ...args) => new Promise(resolve => {
//每个 generator 函数执行会将一个异步函数压入队列。
enqueue(fn, resolve, args);
});
return generator;
}
enqueue 主要是用于处理异步函数的入队操作
//在p-limit中主要用到了yocto-queue的两个方法:enqueue(入队),dequeue(出队)。
//多个 generator 函数会共用一个队列。
import Queue from 'yocto-queue';
// 初始化队列
const queue = new Queue();
// 初始化计数器
let activeCount = 0;
const enqueue = (fn, resolve, args) => {
// 入队,使用run函数对异步函数fn进行包装
queue.enqueue(run.bind(undefined, fn, resolve, args));
// 自执行的async函数,
(async () => {
//await Promise.resolve()也只是返回一个成功的状态。以便于获取最新的activeCount的值
await Promise.resolve();
//如果当前的计数器小于规定的并发数,并且队列中还有未执行的函数,
if (activeCount < concurrency && queue.size > 0) {
//出队并执行run函数
queue.dequeue()();
}
})();
};
为什么使用async?
将异步函数出队执行的操作放入微任务队列,便于获得activeCount的最新值,与concurrency比较
await Promise.resolve() 作用
返回一个成功的状态。以便于获取最新的activeCount的值。
run 控制计数器的个数,执行异步函数
const run = async (fn, resolve, args) => {
activeCount++; //执行异步函数,计数器加1
//执行异步函数,并保存执行后的结果
const result = (async () => fn(...args))();
//执行resolve函数
resolve(result);
try {
//执行后的通过
await result;
} catch {}
//下一步执行函数,必须在await result后面
next();
};
**next **异步函数执行完毕后的后续操作,包括对计数器减一,取出后续的异步任务。
const next = () => {
//计数器减一
activeCount--;
//如果队列中还有任务,那么继续取出执行
if (queue.size > 0) {
queue.dequeue()();
}
};
辅助函数 activeCount(获取当前执行任务的个数)、pendingCount(当前等待任务个数,队列中的个数)、clearQueue(清空队列)
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount,
},
pendingCount: {
get: () => queue.size,
},
clearQueue: {
value: () => {
queue.clear();
},
},
});