问题描述
我试图创建一个工厂函数,该函数以给定的延迟执行特定的异步函数.
I am trying to create a factory function that executes a specific async function with a given delay.
出于这个问题的目的,这将是我所指的异步函数:
For the purposes of this question, this will be the async function I refer to:
/*
* This is a simulation of an async function. Be imaginative!
*/
let asyncMock = function(url) {
return new Promise(fulfil => {
setTimeout(() => {
fulfil({
url,
data: "banana"
});
}, 10000);
});
};
此函数采用 url
,并返回一个包含该URL和一些数据的JSON对象.
This function takes an url
and it returns a JSON object containing that URL and some data.
在我的代码周围,我以以下方式调用了此函数:
All around my code, I have this function called in the following way:
asyncMock('http://www.bananas.pt')
.then(console.log);
asyncMock('http://www.berries.com')
.then(console.log);
//... badjillion more calls
asyncMock('http://www.oranges.es')
.then(console.log);
问题
这里的问题是所有这些调用都是在同一时间进行的,从而使 asyncMoc
正在使用的资源超载.
为避免上述问题,我希望延迟Xms对所有 asyncMoc
的调用的执行.
To avoid the previous problem, I wish to delay the execution of all calls to asyncMoc
by Xms.
这是我假装的图形:
为此,我编写了以下方法:
To achieve this I wrote the following approaches:
- 使用承诺
- 使用setInterval
使用承诺
let asyncMock = function(url) {
return new Promise(fulfil => {
setTimeout(() => {
fulfil({
url,
data: "banana"
});
}, 10000);
});
};
let delayFactory = function(args) {
let {
delayMs
} = args;
let promise = Promise.resolve();
let delayAsync = function(url) {
return promise = promise.then(() => {
return new Promise(fulfil => {
setTimeout(() => {
console.log(`made request to ${url}`);
fulfil(asyncMock(url));
}, delayMs);
});
});
};
return Object.freeze({
delayAsync
});
};
/*
* 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 delayer = delayFactory({
delayMs: 500
});
console.log('running');
delayer.delayAsync('http://www.bananas.pt')
.then(console.log)
.catch(console.error);
delayer.delayAsync('http://www.fruits.es')
.then(console.log)
.catch(console.error);
delayer.delayAsync('http://www.veggies.com')
.then(console.log)
.catch(console.error);
该工厂具有名为 delayAsync
的功能,该功能会将所有对 asyncMock
的调用延迟500毫秒.但是,它也强制执行嵌套等待上一个结果的调用-这是不希望的.
This factory has a function called delayAsync
that will delay all calls to asyncMock
by 500ms.However, it also forces the nest execution of the call to wait for the result of the previous one - which in not intended.
这里的目标是在500ms内对 asyncMock
进行三个调用,并在收到三个相差500ms的响应后10s.
The objective here is to make three calls to asyncMock
within 500ms each, and 10s after receive three responses with a difference of 500ms.
在这种方法中,我的目标是建立一个具有一系列参数的工厂.然后,每500毫秒,计时器将运行一个执行程序,该执行程序将从该数组中获取一个参数并返回一个结果:
In this approach, my objective is to have a factory which has an array of parameters. Then, every 500ms, the timer will run an executor which will take a parameter from that array and return a result with it:
/*
* This is a simulation of an async function. Be imaginative!
*/
let asyncMock = function(url) {
return new Promise(fulfil => {
setTimeout(() => {
fulfil({
url,
data: "banana"
});
}, 10000);
});
};
let delayFactory = function(args) {
let {
throttleMs
} = args;
let argsList = [];
let timer;
/*
* 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 delayAsync = function(url) {
argsList.push(url);
return new Promise(fulfil => {
if (timer === undefined) {
console.log('created timer');
timer = setInterval(() => {
if (argsList.length === 0) {
clearInterval(timer);
timer = undefined;
} else {
let arg = argsList.shift();
console.log('making request ' + url);
fulfil(asyncMock(arg));
}
}, throttleMs);
} else {
//what if the timer is already running? I need to somehow
//connect it to this call!
}
});
};
return Object.freeze({
delayAsync
});
};
/*
* 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 delayer = delayFactory({
delayMs: 500
});
console.log('running');
delayer.delayAsync('http://www.bananas.pt')
.then(console.log)
.catch(console.error);
delayer.delayAsync('http://www.fruits.es')
.then(console.log)
.catch(console.error);
delayer.delayAsync('http://www.veggies.com')
.then(console.log)
.catch(console.error);
// a ton of other calls in random places in code
此代码甚至更糟.它执行3次 asyncMoch
而没有任何延迟,总是使用相同的参数,然后因为我不知道如何完成我的 else
分支,所以它什么也不做.
This code is even worse. It executes asyncMoch
3 times without any delay whatsoever, always with the same parameter, and then because I don't know how to complete my else
branch, it does nothing.
- 哪种方法可以更好地实现我的目标?如何解决?
推荐答案
我要假设您希望 delayAsync
返回的承诺根据 asyncMock 的承诺进行解析代码>.
I'm going to assume you want the promises returned by
delayAsync
to resolve based on the promises from asyncMock
.
如果是这样,我将使用基于诺言的方法并进行如下修改(请参见评论):
If so, I would use the promise-based approach and modify it like this (see comments):
// Seed our "last call at" value
let lastCall = Date.now();
let delayAsync = function(url) {
return new Promise(fulfil => {
// Delay by at least `delayMs`, but more if necessary from the last call
const now = Date.now();
const thisDelay = Math.max(delayMs, lastCall - now + 1 + delayMs);
lastCall = now + thisDelay;
setTimeout(() => {
// Fulfill our promise using the result of `asyncMock`'s promise
fulfil(asyncMock(url));
}, thisDelay);
});
};
确保每次对
asyncMock
的调用至少在上一个调用之后 delayMs
(由于计时器的变化,给定或花费一毫秒),并确保第一个调用为至少延迟了 delayMs
.
That ensures that each call to
asyncMock
is at least delayMs
after the previous one (give or take a millisecond thanks to timer vagaries), and ensures the first one is delayed by at least delayMs
.
带有一些调试信息的实时示例:
Live example with some debugging info:
let lastActualCall = 0; // Debugging only
let asyncMock = function(url) {
// Start debugging
// Let's show how long since we were last called
console.log(Date.now(), "asyncMock called", lastActualCall == 0 ? "(none)" : Date.now() - lastActualCall);
lastActualCall = Date.now();
// End debugging
return new Promise(fulfil => {
setTimeout(() => {
console.log(Date.now(), "asyncMock fulfulling");
fulfil({
url,
data: "banana"
});
}, 10000);
});
};
let delayFactory = function(args) {
let {
delayMs
} = args;
// Seed our "last call at" value
let lastCall = Date.now();
let delayAsync = function(url) {
// Our new promise
return new Promise(fulfil => {
// Delay by at least `delayMs`, but more if necessary from the last call
const now = Date.now();
const thisDelay = Math.max(delayMs, lastCall - now + 1 + delayMs);
lastCall = now + thisDelay;
console.log(Date.now(), "scheduling w/delay =", thisDelay);
setTimeout(() => {
// Fulfill our promise using the result of `asyncMock`'s promise
fulfil(asyncMock(url));
}, thisDelay);
});
};
return Object.freeze({
delayAsync
});
};
/*
* 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 delayer = delayFactory({
delayMs: 500
});
console.log('running');
delayer.delayAsync('http://www.bananas.pt')
.then(console.log)
.catch(console.error);
delayer.delayAsync('http://www.fruits.es')
.then(console.log)
.catch(console.error);
// Let's hold off for 100ms to ensure we get the spacing right
setTimeout(() => {
delayer.delayAsync('http://www.veggies.com')
.then(console.log)
.catch(console.error);
}, 100);
.as-console-wrapper {
max-height: 100% !important;
}
这篇关于如何延迟执行功能,JavaScript的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!