# 队列限流
应用场景类似于在医院排队,假设只有2
个窗口,怎么实现这样一个函数?
数组中是一个个的函数,函数的执行结果是个promise
。
# 实现1
思路是递归,在promise
的then
函数中递归,继续执行新的函数。
const seriesFunc = function (arr, num = 2) {
let len = arr.length;
if (len <= num) {
return Promise.all(arr.map(func => func()));
}
return new Promise(function (resolve, reject) {
let limit = 0; //当前执行的数量,达到限制时就停止循环
let cur = 0; // 当前执行函数的索引
let count = len;
// let curRunArr = new Set();
let callback = function () {
while (limit >=0 && limit < num && cur < len) {
// let jilu = cur;
// curRunArr.add(jilu);
arr[cur]().then(function () {
// curRunArr.delete(jilu);
limit--;
count--;
callback();
if (count === 0) {
resolve();
}
}, reject);
limit++;
cur++;
// console.log(curRunArr);
}
};
callback();
});
};
测试:
const sleep = function (time, res) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('------', res, time);
resolve(res);
}, time);
});
};
const pFunc = function (i) {
return function () {
return sleep(1000 * Math.abs(5 - i), 'p' + i);
// return sleep(1000, 'p' + i);
};
};
const generateArr = function (sum) {
return Array.from(new Array(sum)).map((_, i) => {
return pFunc(i);
});
};
const arr = generateArr(10);
console.time('app');
seriesFunc(arr, 3).then(() => {
console.log('end');
console.timeEnd('app');
});
# 实现2
有个库p-limit
,就是为限制并发而生。其源码较短,大致如下:
const pTry = (fn, ...arguments_) => new Promise(resolve => {
resolve(fn(...arguments_));
});
const pLimit = concurrency => {
if (!((Number.isInteger(concurrency) || concurrency === Infinity) && concurrency > 0)) {
return Promise.reject(new TypeError('Expected `concurrency` to be a number from 1 and up'));
}
const queue = [];
let activeCount = 0;
const next = () => {
activeCount--;
if (queue.length > 0) {
queue.shift()();
}
};
const run = (fn, resolve, ...args) => {
activeCount++;
const result = pTry(fn, ...args);
resolve(result);
result.then(next, next);
};
const enqueue = (fn, resolve, ...args) => {
if (activeCount < concurrency) {
run(fn, resolve, ...args);
} else {
queue.push(run.bind(null, fn, resolve, ...args));
}
};
const generator = (fn, ...args) => {
return new Promise(resolve => {
return enqueue(fn, resolve, ...args);
});
};
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount
},
pendingCount: {
get: () => queue.length
}
});
return generator;
};
是这样使用的:
const limit = pLimit(1);
const sleep = function (time) {
return new Promise((resolve => {
setTimeout(resolve, time);
}));
};
const func1 = async function () {
console.log('---func1');
await sleep(1000);
};
const func2 = async function () {
console.log('---func2');
await sleep(1000);
};
const func3 = async function () {
console.log('---func3');
await sleep(1000);
};
const input = [
limit(func1),
limit(func2),
limit(func3),
];
(async () => {
const result = await Promise.all(input);
console.log(result);
})();