How does JavaScript call web APIs? - javascript

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.

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
}

Nested multilayered async/await doesn't seem to wait

I have a piece of code simplified version of which looks like this:
let dataStorage1; //declare global vars for easier access later on
let dataStorage2;
let stopLight = true; //this variable is used to 'mark' an iteration as successful (= true) or
//failed (= false) and in need of a retry before continuing to the next
//iteration
let delay = 2000; //the standard time for a delay between api calls
async function tryFetch() {
try {
dataStorage1 = await api.fetch('data_type_1'); //fetch needed data trough api, which
//fills the global variable with an
//object
dataStorage2 = await api.fetch('data_type_2'); //do the same
stopLight = true; //change the value of stopLight to true, thus marking this iteration
//as successful
} catch (err) {
console.log(err);
stopLight = false;
}
}
async function fetchData() {
stopLight = true; //change the stopLight to default before execution
await tryFetch(); //fetch data and assign it to variables
//this section is needed for retrial of fetching after a 2s delay if the first attempt was
//unsuccessful, which is repeated until it's either successful or critical error occurred
while (stopLight == false) {
setTimeout(async () => await tryFetch(), delay);
}
}
(async function main() {
await fetchData(); //finally call the function
setTimeout(main, delay); //repeat the main function after 2s
})();
As you can see, self-executing, pseudo-recursive main() calls for await fetchData(), then fetchData() calls for await tryFetch() and finally tryFetch() calls for await api.fetch('~'), as it's defined in the api.
However, once I started the script and paused it after a couple of iterations, I noticed that both dataStorage1 and dataStorage2 remain undefined. If I go through the code step by step in debugger, what happens is that the execution starts at the beginning of fetchData(), moves to the await tryFetch(); line, skips it, and then goes onto the next iteration.
For the reference, if I call dataStorage1/2 = await api.fetch(`~`); in the body of main() directly without any nesting, it works perfectly (unless error occurs, since they are not handled properly).
So, my question is what have I missed?
Indeed, if in an async function you call setTimeout you cannot expect it to perform an await on anything that relates to the callback passed to setTimeout. The call to setTimeout returns immediately, and your while loop is effectively a synchronous loop. It is a so called "busy loop" -- blocking your GUI as it potentially will loop for thousands of times.
As a rule of thumb, use setTimeout only once: to define a delay function, and then never again.
Also avoid using a global variable like stopLight: this is bad practice. Let the async function return a promise that resolves when this is supposed to be true, and rejects when not.
// Utility function: the only place to use setTimeout
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function tryFetch() {
try {
let dataStorage1 = await api.fetch('data_type_1');
let dataStorage2 = await api.fetch('data_type_2');
return { dataStorage1, dataStorage2 }; // use the resolution value to pass results
} catch (err) {
console.log(err);
// retry
throw err; // cascade the error!
}
}
async function fetchData() {
while (true) {
try {
return await tryFetch(); // fetch data and return it
} catch (err) {} // repeat loop
}
}
(async function main() {
let intervalTime = 2000; //the standard time for a delay between api calls
while (true) { // for ever
let { dataStorage1, dataStorage2 } = await fetchData();
// ... any other logic that uses dataStorage1, dataStorage2
// should continue here...
await delay(intervalTime); //repeat the main function after 2s
}
})();
I think the problem is in this line: setTimeout(async () => await tryFetch(), delay);. The await statement inside the callback makes the promise returned by that callback wait, not the whole function. So async () => await tryFetch() is a function that returns a promise, but nothing waits for that promise to complete.
Try replacing that code with something line
await new Promise((resolve) => setTimeout(resolve, delay));
await tryFetch();

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

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