I am trying to make a function that delays the execution of asynchronous functions by X ms.
For the purposes of this demonstration, the following is the async function, which takes an URL:
/*
* This is a simulation of an async function. Be imaginative!
*/
let asyncMock = function(url) {
return new Promise(fulfil => {
setTimeout(() => {
fulfil({
url,
data: "banana"
});
}, 10000);
});
};
My objective here is to have a function, that will take the url
argument of asyncMock
and then call it every X ms or until there are no more arguments left.
Basically, I want every invocation of asyncMock
to be separated by X ms.
As an example, imagine I call asyncMock
20 times in a row. Normally, those 20 calls would be done immediately. What I want, it to make sure that there is Xms of delay between each of the 20 calls.
My idea to solve this, is to have a factory, that will return a promise that will execute the function after X ms.
let throttleFactory = function(args) {
let {
throttleMs
} = args;
let promise = Promise.resolve();
let throttleAsync = function(url) {
return promise.then(() => {
setTimeout(anUrl => {
return new Promise( fulfil => {
fulfil(asyncMock(anUrl));
});
}, throttleMs, url);
});
};
return Object.freeze({
throttleAsync
});
};
Ideally I would use this factory like in the example bellow:
let throttleFuns = throttleFactory({
throttleMs: 2000
});
console.log('running');
throttleFuns.throttleAsync('http://www.bananas.pt')
.then(console.log)
.catch(console.error);
throttleFuns.throttleAsync('http://www.fruits.es')
.then(console.log)
.catch(console.error);
throttleFuns.throttleAsync('http://www.veggies.com')
.then(console.log)
.catch(console.error);
// a ton of other calls in random places in code
The problem here is that my throttleAsync
fucntion outputs undefined
three times immediately. I believe this might be because I am not defining promise
properly.
How can I fix this code to work as intended?
Because throttleAsync
returns the result of calling promise.then
, and the then
callback doesn't return anything. This makes the promise created by then
resolve with the value undefined
.
You probably meant to have it return the new promise you're creating, but you're not doing that until the setTimeout
callback. You want to do it prior (but there's more, keep reading):
let throttleAsync = function(url) {
return promise.then(() => {
return new Promise( fulfil => {
setTimeout(anUrl => {
fulfil(asyncMock(anUrl));
}, throttleMs, url);
});
});
};
There's also no reason to pass the URL through setTimeout
like that, so:
let throttleAsync = function(url) {
return promise.then(() => {
return new Promise( fulfil => {
setTimeout(() => {
fulfil(asyncMock(url));
}, throttleMs);
});
});
};
Originally I though the promise
there was unnecessary, but you've clarified that you want to ensure that repeated calls are "spaced out" by throttleMs
. To do that, we'd use the above, but update promise
:
let throttleAsync = function(url) {
return promise = promise.then(() => {
// ^^^^^^^^^
return new Promise( fulfil => {
setTimeout(() => {
fulfil(asyncMock(url));
}, throttleMs);
});
});
};
This way, the next call to asyncThrottle
will wait until the previous one has fired before starting the next.
Live Example:
const throttleMs = 1000; const asyncMock = url => url; let promise = Promise.resolve(); let throttleAsync = function(url) { return promise = promise.then(() => { // ^^^^^^^^^ return new Promise( fulfil => { setTimeout(() => { fulfil(asyncMock(url)); }, throttleMs); }); }); }; console.log('running'); throttleAsync('http://www.bananas.pt') .then(console.log) .catch(console.error); throttleAsync('http://www.fruits.es') .then(console.log) .catch(console.error); throttleAsync('http://www.veggies.com') .then(console.log) .catch(console.error);
Here's your problem:
setTimeout(anUrl => {
return new Promise( fulfil => {
fulfil(asyncMock(anUrl));
});
}, throttleMs, url);
What you're doing here is you return a promise from the setTimeout callback. The return value of functions run by setTimeout are ignored so no one will get that value.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.