Repeat function itself without setInterval based on its finish - javascript

I need my program to repeat itself continuously. My program starts to fetch proxies from servers and saves them to a database and then send those saved proxies to another server again. So I don't know how long it takes for my program to do this task.
I wanna know what happens if any problem happens that makes this startJob() function take more than 30 seconds.
Does setInterval call it again or waits for function to finish?
What's the best approach for my program to repeat itself after it's done without setInterval?
(for exmaple startJob() being called again after it's done.)
I was wondering if it is ok to put this function in a loop with a big number like:
for ( let i = 0 ; i < 999999999 ; i ++ ) {
await startJob()
}
Here is my code:
const startJob = async () => {
await postProxyToChannel()
grabProxies()
}
setInterval(function(){
startJob()
}, (30000))
grabProxies() takes about 10 seconds and postProxyToChannel() takes about 5 seconds on my server.

No matter what happens inside startJob, setInterval will call it every 30 seconds. This means that postProxyToChannel will be called every 30 seconds. If that function throws, you'll get an unhandled Promise rejection, but the interval will continue.
Even if postProxyToChannel takes, say, 45 seconds, that won't prevent startJob from being called again before the prior startJob has completed.
If you want to make sure that startJob is only called 30 seconds after it finishes, you could await it in your for loop, then await a Promise that resolves every 30 seconds:
(async () => {
for ( let i = 0 ; i < 999999999 ; i ++ ) {
await startJob();
await new Promise(resolve => setTimeout(resolve, 30000));
}
})()
.catch((err) => {
console.log('There was an error', err);
});
But it would probably make more sense just to have a recursive call of startJob, eg:
const startJob = async () => {
try {
await postProxyToChannel();
} catch(e) {
// handle error
}
grabProxies();
setTimeout(startJob, 30000);
};
startJob();

Yup an infinite loop sounds good, that can be compared with a timer to pause the loop:
const timer = ms => new Promise(resolve => setTimeout(resolve, ms));
(async function() {
while(true) {
await postProxyToChannel();
await grabProxies();
await timer(30000);
}
})();
Now that loop will run the task, wait 30secs, then do that again. Therefore the loop will not run every 30secs but will usually take longer. To adjust that, you could measure the time the task took, then await the rest of the time:
const start = Date.now();
await postProxyToChannel();
await grabProxies();
await timer(30000 - (Date.now() - start));

Related

JavaScript (React): How to extend async function's await to a given time

I would like to have a async function that sends a request to a server and awaits the response. It will then wait upto 1000ms to ensure that an animation has played out.
i was wondering if I could combine these 2 tasks somehow so they add up to having waited 1000ms in total dynamically.
My current code:
const loadingHandler = async (func, target) => {
setIsLoading(true);
await new Promise(r => setTimeout(r, 1000));
await func(target);
setIsLoading(false);
};
Some examples to further explain what exactly I mean: the function calls func() and gets a response after (lets say) 200ms. Now the timeout is only supposed to be 800ms. If the func() returns after 20ms the timeout is supposed to be 980ms. If func() takes longer than 1000ms it should continue immedietly after getting the response and not wait additionally.
So is something like this 'stretcher await function' possible?
Sure, just remember how long it's been so far, then delay that much longer:
const loadingHandler = async (func, target) => {
setIsLoading(true);
const started = Date.now();
await func(target);
const elapsed = Date.now() - started;
const delayFurther = 1000 - elapsed;
if (delayFurther > 0) {
await new Promise(r => setTimeout(r, delayFurther));
}
setIsLoading(false);
};
That said, holding up the user because an animation hasn't finished might not be the best UX. Perhaps you could make the animation finish more quickly when func is already done. (Humans tend to notice delays > about 80-100ms, a bit less when we're younger.)
An alternative to Mr Crowder's perfectly valid solution: Rather than wait for the promise to finish and then checking if we need to start a new one, start two promises at the same time and wait for them both. Has the same effect, but makes the code shorter:
const loadingHandler = async (func, target) => {
setIsLoading(true);
await Promise.all([
func(target),
new Promise(r => setTimeout(r, 1000)),
]);
setIsLoading(false);
}
In your code you starts first promise (interval), waits for it to finish, then starts second (func) and wait to finish. You should start both promises and wait for it together.
const loadingHandler = async (func, target) => {
setIsLoading(true);
const p1 = new Promise(r => setTimeout(r, 1000));
const p2 = func(target);
await Promise.all([p1,p2]);
setIsLoading(false);
};

How do I make a Javascript loop wait for existing iteration to finsih before starting the next?

How do I make the below loop wait for current iteration to finish before it starts the next iteration?
var minute = 0
alert ("Kick Off");
var refreshIntervalId = setInterval(function() {
if (minute < 91) {
// lots of code with Ajax calls
// can take from a fraction of a second to 30 seconds to run code
// depending on conditions
} else {
clearInterval(refreshIntervalId);
}
minute++
}, 1000);
I usually use functions and '.done' to execute code after pre-requisite code has finished. I have also used functions within animations to execute code after the animation has finished.
However I can't get my head around how you make the code to wait for an iteration of a loop to finish. If I put the pre-requisite code into a function (all the code within the loop) and then use .done, there is only the closing brackets of the loop left to put within a '.done' function - which obviously will not work.
Can anyone solve this conundrum?
You can make a while loop in an async function instead and use await. In the code below, the while-loop waits at least 2 seconds at each iteration but at most as long as the task takes.
const sleep = time => new Promise(resolve => setTimeout(resolve, time))
async function main() {
while (true) {
console.log("new iteration")
const task = new Promise((resolve, reject) => {
// things that take long go hereā€¦
const duration = Math.random()*4000
setTimeout(() => {
console.log(`done, task took ${Math.round(duration)}ms`)
resolve()
}, duration)
})
// wait until task is finished but at least 2 seconds
await Promise.all([task, sleep(2000)])
}
}
main()
To bring Matthias's elegant up-voted answer to life in your specific situation:
var minute = 0
// reusable function to wait the specified amount of time (1000ms in your case)
const sleep = time => new Promise(resolve => setTimeout(resolve, time))
alert ("Kick Off");
while (minute < 91)
{
const processThisMinuteTask = new Promise((res, rej) => {
// lots of code with Ajax calls
// can take from a fraction of a second to 30 seconds to run code
// depending on conditions
// NOTE: use AWAIT for all Ajax calls
res();
});
// will wait for both:
// a) your processThisMinuteTask to finsih
// b) 1000ms to pass
// So, will mean that this iteration will wait for whichever of those is longest
await Promise.all([processThisMinuteTask, sleep(1000)]);
minute++;
}
P.S. Nothing wrong with the "Alert" if what you want to do is prompt the user that kick-off is about to start, but will not start until they acknowledge the alert.
Further to comment about "await is a reserved identifier", the enclosing function needs to be declared as async.
function playMatch() {
// will fail
await myOtherFunction();
// more code
}
Needs to be:
async function playMatch() {
// will now wait for myOtherFunction to finish before continuing.
await myOtherFunction();
// more code
}

How does JavaScript call web APIs?

As far as I know, JavaScript's normal behavior when I call a web API just like the setTimeout 4 times:
it should call the first one then add it to a queue waiting for the call stack to be empty .. repeatedly it will do the same for all other apis .. so the second function should wait till the first executes then start to be called .. which means that it should take the second function 2 seconds or more, then it should take the third function three seconds or more ... and so on ...
what am I missing !?
Try to use async await and wait for each actions to be done.
const sleep = (ms) => new Promise(e => setTimeout(e, ms));
const logSeconds = async () => {
await sleep(1000);
console.log(new Date().toLocaleTimeString());
}
const main = async () => {
await logSeconds();
await logSeconds();
await logSeconds();
}
main()
var TimeOutOnTimeOut = () => {
setTimeout(() => {
console.log("Like this!");
TimeOutOnTimeOut();
}, 3000);
};
TimeOutOnTimeOut();
They'll all call at the same time, you could, however, call the next after the first is finished as described.

Understanding NodeJs await with setTimeout() method

I'm fairly new to NodeJs and understanding the concept of async/await. Please correct me if I'm wrong - await keyword blocks the code until it gets a resolved value. For example:
const sampleFunction = async () => {
const result = await someAsynFunctionReturningPromise();
console.log('Log is printed here!');
}
In the above code, the compiler stops the code at 'const result = await someAsynFunctionReturningPromise();' until 'someAsynFunctionReturningPromise()' gets resolved, right?
With the above assumption, I tried the below setTimeout() code:
const sampleFunction = async () => {
const result = await setTimeout(()=>{
console.log('This is printed after 2 seconds');
},2000);
console.log('Log is printed here!');
}
However, the above code doesnt await till setTimeout() is resolved, it skips to the next line, printing 'Log is printed here!'.
Can anyone please help me understand if my understanding is wrong?
In order for await to work with setTimeout, setTimeout needs to return a promise. Please see this: How to make a promise from setTimeout
This is where the event loop of node JS working comes in. In the first place your
someAsynFunctionReturningPromise()
is returning a promise where in the second code setTimeout() does not return you promise. Also when nodejs eventloop gets such a function like setTimeout() it will put it in callstack and then to taskqueue where it will wait for 2000ms and goes for the next line of execution, that is why it is printing
console.log('Log is printed here!');
when 2000ms are over the setTimeout will be kept from taskqueue to callstack and it will execute
console.log('This is printed after 2 seconds');
You wrote your own answer. You wrote
await keyword blocks the code until it gets a resolved value
In your code you never resolved anything so of course it won't work
A setTimeout that resolves is
function wait(ms = 0) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, ms);
};
}
or the terse version
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
Example
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
const sampleFunction = async () => {
for(;;) {
await wait(2000);
console.log('time since page loaded:', performance.now() / 1000 | 0);
}
};
sampleFunction();

While Loop inside ASYNC AWAIT

I have some code that continuously updates a series of objects via network calls looks like this. I was wondering if this is bad practice and if there might be a better way. I cant use Set Interval as the time between MakeAsyncCall replies is variable and can cause a leak if the time to make the call is longer than the delay. I will be using this info to update a UI. Will this cause blocking? What are your thoughts? Let me know if you need more info.
let group = [item1, item2, item3];
// Start Loop
readForever(group, 100);
// Function to Delay X ms
const delay = ms => {
return new Promise((resolve, _) => {
const timeout = setTimeout(() => {
resolve();
}, ms);
});
};
// Function to continuously Make Calls
const readForever = async (group, ms) => {
while(true) {
// Make Async Call
for (let item of group) {
await MakeAsyncCall(item);
}
// Wait X ms Before Processing Continues
await delay(ms);
}
};
The given code won't cause any UI blocking. And is a valid way to update the UI continually.
Instead of a loop you could write it that way:
const readForever = async (group, ms) => {
// Make Async Call
for (let item of group) {
await MakeAsyncCall(item);
}
// Wait X ms Before Processing Continues
await delay(ms);
if (true) { // not needed, but there you could define an end condition
return readForever(group, ms);
}
};
In addition to the comment about the delay function:
You could directly pass the resolve to setTimeout, and because you do not cancel the Timeout anywhere you do not need to store the result setTimeout in a variable.
const delay = ms => {
return new Promise(resolve => setTimeout(resolve, ms));
};

Categories

Resources