【发布时间】:2017-07-22 08:09:28
【问题描述】:
背景
我正在向服务器发出一批 HTTP GET 请求,我需要限制它们以避免杀死糟糕的服务器。就我的演示而言,这将是 GET 方法:
/*
* This function simulates a real HTTP GET request, that always takes 1 seconds
* to give a response. In this case, always gives the same response.
*/
let mockGet = function(url) {
return new Promise(fulfil => {
setTimeout(
url => {
fulfil({ url, data: "banana"});
},
1000, url);
});
};
我在几个地方和不同的上下文中使用mockGet,所以现在我想改用一个使用mockGet 的getUrl 函数,但这会将其限制在合理的速度。
我的意思是我需要一个函数,当多次调用时,它总是按给定的顺序执行,但在不同的执行之间有给定的延迟。
研究
我的第一个尝试是使用 underscorejs 和 lodash 之类的库来实现:
但它失败了,因为它们没有提供我所追求的功能。
事实证明,我需要将每个调用保存在一个列表中,以便以后方便时调用它。按照这个逻辑,我找到了另一个答案:
这有点回答我的问题,但有几个问题我打算解决。它具有全局变量,没有关注点分离,迫使用户了解内部机制......我想要一些更干净的东西,一些在 underscorejs 或 lodash 行中的东西,将所有这些隐藏在一个易于使用的函数后面。
代码
我对这个挑战的看法是使用带有 Promises 的工厂模式来返回我需要的东西:
let getFactory = function(args) {
let {
throttleMs
} = args;
let argsList = [];
let processTask;
/*
* Every time this function is called, I add the url argument to a list of
* arguments. Then when the time comes, I take out the oldest argument and
* I run the mockGet function with it, effectively making a queue.
*/
let getUrl = function(url) {
argsList.push(url);
return new Promise(fulfil => {
if (processTask === undefined) {
processTask = setInterval(() => {
if (argsList.length === 0) {
clearInterval(processTask);
processTask = undefined;
}
else {
let arg = argsList.shift();
fulfil(mockGet(arg));
}
}, throttleMs);
}
});
};
return Object.freeze({
getUrl
});
};
这是我正在遵循的算法:
- 每次调用
getUrl,我都会将参数保存在一个列表中。 -
然后,我检查
setInterval计时器是否已启动。- 如果没有,则从给定的延迟开始,否则我什么也不做。
-
在执行时,
setInterval函数会检查参数队列。- 如果它是空的,我会停止
setInterval计时器。 - 如果它有参数,我会从列表中删除第一个参数,并使用真正的
mockGet函数及其结果来实现 Promise。
- 如果它是空的,我会停止
问题
虽然所有的逻辑似乎都已经到位,但这还没有奏效……还没有。当我使用它时会发生什么:
/*
* All calls to any of its functions will have a separation of X ms, and will
* all be executed in the order they were called.
*/
let throttleFuns = getFactory({
throttleMs: 5000
});
throttleFuns.getUrl('http://www.bananas.pt')
.then(console.log);
throttleFuns.getUrl('http://www.fruits.es')
.then(console.log);
throttleFuns.getUrl('http://www.veggies.com')
.then(console.log);
// a ton of other calls in random places in code
只打印第一个响应,其他的不打印。
问题
- 我做错了什么?
- 如何改进此代码?
【问题讨论】:
-
有趣的是你想通过调用来实现 - 如果这是某种长轮询,你应该切换到 websockets
-
抱歉,不允许使用网络套接字:P
标签: javascript node.js ecmascript-6 promise throttling