How to wait end of promise but with timeout - javascript

I have a REST API coded with Node.js and there is one route that applies NTFS Rights to folders. The request can take 1 second but can also last several minutes (it depends on the size of the folder).
To summarize, if the rights take less than 5 seconds to be applied, I want to return the value with the code 200.
If the rights have not finished being applied I would like to return the value with the code 202
...
inProgress = true;
ApplyRights()
.then(() => {
inProgress = false
}
// Here I want to return as fast inProgress = false otherwise wait a bit but max 5s
return ...;
I tried to wait a bit with setTimeout like this:
const sleep = s => new Promise(resolve => {
setTimeout(resolve, s * 1000)
});
let nbCheck = 0;
while (inProgress && nbCheck < 5){
await sleep(1)
nbCheck++;
}
But setTimeout is not called before the end of my previous promise (ApplyRights).
I read here that promise are executed before setTimeout
So, i tried to find a solution without setTimeout and I tried this: (I know it's not very elegant)
let dateStop = Date.now() + 5 * 1000;
while (dateStop = Date.now() && inProgress){}
return ...;
But in this case the .then() of ApplyRights is only reached at the end of the 5s.
Is there a way to let my promise ApplyRights do its job. And if the job take time, wait maximum 5 seconds before returning the response. If the job is quick, I want to return the responses without waiting.

You can use Promise.race:
Promise.race([ApplyRights(), sleep(5)]).then(result => {
if (result === undefined) { // Timeout of 5 seconds occurred
// Some message to the user?
} else { // Got reply within 5 seconds
// Do something with the result
}
});
Do realise however, that this does not interrupt the work of ApplyRights(), which eventually may still do the job.

Related

How to pause a javascript method without wasting computation?

Essentially, how do you pause javascript execution without having to waste computation on something like a while loop?
For example, say I want to only perform a function next after 10 seconds and not interrupt other processes or waste computation? setTimeout won't work because I want the processes to actually pause / not continue any operations during that time.
const next = () => console.log("next");
/** pause for 10 seconds */
next()
Also what if I want to only run a method conditionally, where every couple seconds or something I can either abort the operation or continue. Notably I don't mean to use setInterval because in that case it's not actually conditionally pausing the actual javascript execution.
const next = () => console.log("next");
const start = new Date();
const startSeconds = now.getSeconds();
const checkEvery = 1; // seconds
const requiredDiff = 10; // seconds
const checker = () => {
const now = new Date();
let secondsNow = now.getSeconds();
secondsNow < startSeconds ? secondsNow += 60 : null;
const diff = secondsNow - startSeconds;
if (diff < 10) {
return false
}
return true;
}
/** something that runs checker every 1 seconds and pauses computation until checker() returns true and then runs next() */
A good way to do this is by creating a block in the pipe. i.e.
process -> pause -> next so that you can know that the flow of the program won't continue until a certain criteria is met
You can do this by pausing things inline
...
await pause();
...
where pause is something that does a timed-out promise or something along those lines. I don't know too much about this method, but it seems relatively complicated.
Another way to do this is by stopping the execution inline with a while loop
...
// pause for 10 seconds
let result;
while (!result) { result = checker() };
...
but as you alluded to, this wastes a lot of operations and can interfere with other actions in the background from running properly. Another thing is you can't only check checker every 1 second.
I suggest you do the following instead:
const max = 20; // max number of recursive calls (i.e. timeout after 20 seconds)
// checker is a function that returns true or false and is agnostic to this implementation
// timeout is the time (in milliseconds) to wait before running the checker again
// next is the next step in your pipeline that you want to prevent from e
const pause = async (checker, timeout, next, calls = 0) => {
if (calls > max) return; // prevents stack overflow
const result = await checker(); // just in case your checker is async
// if the condition was met then continue on to the next stage in your pipeline
// if the condition was not met then run this method again to re-check in timeout
result ? next() : setTimeout(() => pause(checker, timeout, next, calls + 1), timeout)
}
// with the functions you provided...
pause(checker, 1000, next)
pause will only execute operations when the timeout is met, and it won't allow the program to continue to the next stage until the checker is met.

How to limit async task until theres no job in loop?

In my code i trying to learn async, i have 700 async task to do, the job will done in random time.
My question how to limit async task in loop?
Let say i want it to do 30 job at start time. and watch event each async task done it will start 1 task to fill limit task 30 at time again until 700 task, or theres no task again.
For now loop will execute all async task in same time. its not i want.
function JobdeskAsync(){
console.log(Math.floor(Math.random() * 1000));
}
function finishedTime(max, min){
return Math.floor(Math.random() * (max - min) ) + min;
}
for ( let i = 0; i < 700; i++){
setTimeout(JobdeskAsync, finishedTime(5000, 1000));
}
#Wendelin has a great example there. For how it directly applies to your application, you currently are:
Looping through 700 asynchronous Jobs
Executing them all immediately (to finish as and when)
What you need to do is be able to recognise when you have reached your pool maximum (30) and not add/execute any more until that pool depletes. I'm not writing the code for you because there's a thousand ways to do it, but at it's base what you need:
Functionality to store executing jobs (call it a "store")
Functionality to add jobs to the store
Functionality to see if that store is full
Combine 2/3 so that when #3 is not true you can do #2
You need to start 30 jobs that continue processing data until there is no more.
Here there is a queue of data that each job pulls from. If there is no data left, it returns a simple promise that resolves to 'done'. If there is data, it processes it and returns the promise from a new call to createJob().
const queue = [];
// fill queue with data to process
for (let i = 0; i < 70; i++) {
queue.push(i);
}
function createJob() {
// pull data to work on from the queue
const data = queue.shift();
// if no data is remaining, return promsie that resolves to 'done'
if (data === undefined) {
return Promise.resolve('done');
}
// otherwise we do some work on the data
return new Promise((resolve, reject) => {
// simulate work by waiting 500-1500ms
setTimeout(() => {
console.log(`handled ${data}`);
// this resolves our promise with another promise, createJob()
resolve(createJob());
}, 500 + Math.random() * 1000);
});
}
// create 30 jobs and wait for all to complete, each job will
// create a new job when it has finished and chain the results
const jobs = [];
for (let i = 0; i < 30; i++) {
jobs.push(createJob());
}
console.log('30 jobs running...');
Promise.all(jobs).then(() => console.log('ALL JOBS COMPLETED'));

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
}

Repeat function itself without setInterval based on its finish

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));

Using setInterval() to do simplistic continuous polling

For a simple web app that needs to refresh parts of data presented to the user in set intervals, are there any downsides to just using setInterval() to get a JSON from an endpoint instead of using a proper polling framework?
For the sake of an example, let's say I'm refreshing the status of a processing job every 5 seconds.
From my comment:
I would use setTimeout [docs] and always call it when the previous response was received. This way you avoid possible congestion or function stacking or whatever you want to call it, in case a request/response takes longer than your interval.
So something like this:
function refresh() {
// make Ajax call here, inside the callback call:
setTimeout(refresh, 5000);
// ...
}
// initial call, or just call refresh directly
setTimeout(refresh, 5000);
A simple non-blocking poll function can be implemented in recent browsers using Promises:
var sleep = duration => new Promise(resolve => setTimeout(resolve, duration))
var poll = (promiseFn, duration) => promiseFn().then(
sleep(duration).then(() => poll(promiseFn, duration)))
// Greet the World every second
poll(() => new Promise(() => console.log('Hello World!')), 1000)
You can do just like this:
var i = 0, loop_length = 50, loop_speed = 100;
function loop(){
i+= 1;
/* Here is your code. Balabala...*/
if (i===loop_length) clearInterval(handler);
}
var handler = setInterval(loop, loop_speed);
Just modify #bschlueter's answer, and yes, you can cancel this poll function by calling cancelCallback()
let cancelCallback = () => {};
var sleep = (period) => {
return new Promise((resolve) => {
cancelCallback = () => {
console.log("Canceling...");
// send cancel message...
return resolve('Canceled');
}
setTimeout(() => {
resolve("tick");
}, period)
})
}
var poll = (promiseFn, period, timeout) => promiseFn().then(() => {
let asleep = async(period) => {
let respond = await sleep(period);
// if you need to do something as soon as sleep finished
console.log("sleep just finished, do something...");
return respond;
}
// just check if cancelCallback is empty function,
// if yes, set a time out to run cancelCallback()
if (cancelCallback.toString() === "() => {}") {
console.log("set timout to run cancelCallback()")
setTimeout(() => {
cancelCallback()
}, timeout);
}
asleep(period).then((respond) => {
// check if sleep canceled, if not, continue to poll
if (respond !== 'Canceled') {
poll(promiseFn, period);
} else {
console.log(respond);
}
})
// do something1...
console.log("do something1...");
})
poll(() => new Promise((resolve) => {
console.log('Hello World!');
resolve(); //you need resolve to jump into .then()
}), 3000, 10000);
// do something2...
console.log("do something2....")
I know this is an old question but I stumbled over it, and in the StackOverflow way of doing things I thought I might improve it. You might want to consider a solution similar to what's described here which is known as long polling. OR another solution is WebSockets (one of the better implementations of websockets with the primary objective of working on all browsers) socket.io.
The first solution is basically summarized as you send a single AJAX request and wait for a response before sending an additional one, then once the response has been delivered, queue up the next query.
Meanwhile, on the backend you don't return a response until the status changes. So, in your scenario, you would utilize a while loop that would continue until the status changed, then return the changed status to the page. I really like this solution. As the answer linked above indicates, this is what facebook does (or at least has done in the past).
socket.io is basically the jQuery of Websockets, so that whichever browser your users are in you can establish a socket connection that can push data to the page (without polling at all). This is closer to a Blackberry's instant notifications, which - if you're going for instant, it's the best solution.

Categories

Resources