End a function only after the end of my interval. (Javascript) - javascript

I have a question regarding a personal project I'm working. I need to create a fade-in and fade-out effect for an object without CSS, and I'm having a hard time with my intervals and function, because I need to finish the fadeout function before moving to the next set of functions.
// Interval parameters
var bgtInterval = [0, false, 50];
// Fade functions will change fill following brightStep array values.
function fadeOut(){
let i = 11;
OBJECT.setAttribute("fill", brightSteps[i]);
bgtInterval[1]=true;
bgtInterval[0] = setInterval(function(){
FULLCLOCK.setAttribute("fill", brightSteps[i]);
if (i<=0){
clearInterval(bgtInterval[0]);
bgtInterval[1]=false;
}
i--;
}, bgtInterval[2]);
}
function fadeIn(){
let i = 0;
OBJECT.setAttribute("fill", brightSteps[i]);
bgtInterval[1]=true;
bgtInterval[0] = setInterval(function(){
FULLCLOCK.setAttribute("fill", brightSteps[i]);
if (i>=11){
clearInterval(bgtInterval[0]);
bgtInterval[1]=false;
}
i++;
}, bgtInterval[2]);
}
-
function resetSettings(id){
fadeOut(); // I need fadeOut to finish before I move forward
displayReset();
displayShow();
fadeIn(); // I need fadeIn to finish before I move out of this function
}
//THE MAIN FUNCTION//
resetSettings();
moreFunctionsHere();
Thank You!!!

Use Promises and async/await to achieve this very simply
var bgtInterval = [0, false, 50];
// Fade functions will change fill following brightStep array values.
function fadeOut(){
// return the Promise
return new Promise(resolve => {
let i = 11;
OBJECT.setAttribute("fill", brightSteps[i]);
bgtInterval[1]=true;
bgtInterval[0] = setInterval(function(){
FULLCLOCK.setAttribute("fill", brightSteps[i]);
if (i<=0){
clearInterval(bgtInterval[0]);
bgtInterval[1]=false;
// resolve when done
resolve();
}
i--;
}, bgtInterval[2]);
});
}
function fadeIn(){
// return a Promise
return new Promise(resolve => {
let i = 0;
OBJECT.setAttribute("fill", brightSteps[i]);
bgtInterval[1]=true;
bgtInterval[0] = setInterval(function(){
FULLCLOCK.setAttribute("fill", brightSteps[i]);
if (i>=11){
clearInterval(bgtInterval[0]);
bgtInterval[1]=false;
// resolve when done
resolve();
}
i++;
}, bgtInterval[2]);
});
}
// use async so we can await a Promise
async function resetSettings(id){
// wait for fadeOut to finish
await fadeOut();
displayReset();
displayShow();
await fadeIn();
}
//THE MAIN FUNCTION//
// as we are in global scope, you can't use `async` ... so use Promise.then instead
resetSettings().then(moreFunctionsHere);
You'll see that I'm using .then in the last line, because in the global scope you can't use await
You could, however do something like
(async () => {
await resetSettings();
moreFunctionsHere();
})();
an async IIFE so you can await

Related

Stop a function running from ANOTHER function in JavaScript

What I want to do is stop a function running from ANOTHER function (in JavaScript). here is an example of what I would like to do:
async function process1(){ //this is a normal function running
//here is a long list of instruction that can take some time (~ 30s)
}
function STOPprocess1(){
process1().Stop; //this is pseudoCode !
}
When I call STOPprocess1() , I want the process1 function to stop running.
You could try something like this:
var flag = true;
async function process1(){ //this is a normal function running
while(flag){
await sleep(50); //we suppose we have the function sleep.
console.log("the function process1 is running...");
}
}
function STOPprocess1(){
flag = false;
}
But you may have problems with the scope...
Use a global variable to stop process 1
let isRunning = true;
async function process1(){ //this is a normal function running
while(isRunning){
await sleep(50); //we suppose we have the function sleep.
console.log("the function process1 is running...");
}
}
function STOPprocess1(){
isRunning = false;
}
How about using generator functions for that? If you yield, defer the call to .next() to a microtask, thus making it possible to interrupt:
function interruptable(gen) {
return function(...args) {
let timer, it = gen(...args);
function next() {
const { done } = it.next();
if(!done) timer = setTimeout(next, 0);
}
next();
return () => clearTimeout(timer);
};
}
const task = interruptable(function* () {
while(true) {
console.log("running");
yield;
}
});
const stop = task();
setTimeout(stop, 1000);
I would suggest setInterval over the use of a while loop (while(true) is generally frowned upon). You can then use clearInterval to stop execution.
let intervalId;
async function process1(){ //this is a normal function running
intervalId = setInterval(() => console.log("the function process1 is running..."), 50);
}
function STOPprocess1(){
clearInterval(intervalId)
}
while(true) might break your browser or whatever you're using. You can use setInterval() instead and use clearInterval() to stop the process.
//before starting the function
var process1;
//when starting the function
var process1 = setInterval(function() {
console.log("the function process1 is running...");
}, 50);
//to stop the function
function STOPprocess1(){
clearInterval("process1");
}
var cnt = 1;
var running = true;
process1();
async function process1(){
if(running){
await new Promise(resolve => setTimeout(resolve, 50));
document.getElementById("tstArea").innerHTML="this: "+ cnt;
cnt++;
process1();
}
}
function stopOtherFn(){
running = false;
}
<div id="tstArea">
</div>
<button onClick="stopOtherFn()">StopThat</button>
here is a rough mockup that seems to accomplish what you are looking for.
If you want to iterate something without stopping instead another function asks for it:
I would not do that. I believe every function should control itself. If the function dedicated to stop another function append to fail, the cost in term of ressources and time to fix it may become problematic.
Instead, I'd create a function calling another function after checking something else (cookie, data, variable).
const checkSomething = () => {
// var in parameter, cookie, data from API, etc
// if necessary throw new Error
if (something) return true;
return false;
};
const something = () => {
console.log('I did that');
};
const periodicallyDoSomething = () => {
try {
let continueDoingSomething = checkSomething();
while (continueDoingSomething) {
something();
continueDoingSomething = checkSomething();
}
} catch (e) {
console.error(e.message);
} finally {
console.log('I did it!');
}
};
// run it, or export it as in module.exports
If you want a function to do something that takes a lot if time while still being able to stop it externally, like a classic CTRL+C:
This should be inside your function.
I believe that a function dedicated to do something should be the same function finishing it.
Try/catch/finally, like I used it juste before, may be interesting.
Have a happy coding time. :)

For Loop SetTimeout Works Only 1 Time

I have a button with click function below. It has 3 nested for loop, and 1 setTimeout function.
The below loops are looping 5 times. I want below code to work(5x5) total 25 seconds of execution time, and each 5 seconds console output should be "Waited" printed.
However below code works only 5 seconds, and immediately prints "5 hello". Without changing my for loop structure, how can I make it work as I want?
jQuery("#btn_trendyolStocksSYNC").click(function() {
for(var product in all){
var colors = all[product];
for(var singleColor in colors[0]){
var size = colors[0][singleColor];
for(var index in size){
var singleSize = size[index];
setTimeout(function (){
console.log('Waited');
}, 5000);
}
}
}
});
Edit: I don't use the for loop with indexes, so solutions for number indexed for loops are not working for me.
You could try by adding await and a Promise:
jQuery("#btn_trendyolStocksSYNC").click(async function() {
for(var product in all){
var colors = all[product];
for(var singleColor in colors[0]){
var size = colors[0][singleColor];
for(var index in size){
var singleSize = size[index];
await new Promise(resolve => setTimeout(function (){
console.log('Waited');
resolve();
}, 5000));
}
}
}
});
What this does is simply tell your loop to stop and only continue once the Promise object calls its resolve parameter function. That way your delay should simply happen before the next iteration. This is the important code:
await new Promise(resolve => setTimeout(function (){
console.log('Waited');
resolve();
}, 5000));
It simply creates a Promise that we will resolve once the timeout has let 5000 milliseconds pass. Then we tell our loop to simply await that completion before continuing to the next item.
Note You also need to add async to your handler function, so javascript knows that this function can wait and take as long as it needs to.
The setTimeout(); function is asynchronous, meaning that your script will not wait for it to finish before moving on. That's why it has a callback.
Try something like this: (not the best method)
//delayed loop
var i = 1;
function loop() {
//wait 5 secs
setTimeout(function() {
console.log(i);
if(i>5) {
//cancel
return;
}
loop();
i++;
return;
}, 1000);
if(i>5) {
//cancel function
return;
}
}
//do the loop
loop();
Like what somethinghere said, you could put the setTimeout in the if statement.
Of course to do something after the loop ends you need a callback function.
you can use setInterval and clearInterval.
var n=0;
var a = setInterval(()=>{
console.log("Waited");
n++; if(n==5){clearInterval(a);}
},5000);

Why functionalities runs sequentially even though they are being called asynchronously inside a loop?

I am creating a wrapper function which will take array of functions and executes every function in a parallel manner so thought of using setTimeout but still functions are running sequentially. I suspect it could be because of closure that is being used to call SetTimeout. But why does it matter since setTimeout is async anyway?
// some blocking functionality
var withDelay = function (a) {
var currentTime = new Date().getTime(), delay = 5000;
while (currentTime + delay >= new Date().getTime()) {
}
console.log(a+"I am with delay");
}
// some non blocking functionality
var withoutDelay = function(a) {
console.log(a+"I am with no delay");
}
var fnArr = [withDelay, withoutDelay]; //array of functions
var args = ["Hi,"]; // arbitrary params
for( var i=0; i < fnArr.length; i++) {
var fn = fnArr[i];
(function(f,arg) {
return setTimeout(function(){ return f.apply(f,arg) },0);
})(fn,args)
}
Expected output:
Hi,I am with no delay
Hi,I am with delay
but Actual output is:
Hi,I am with delay
Hi,I am with no delay
JS runs on a single thread, Your function will not run parallelly. It will only run one at a time. Since you have scheduled both the functions with 0 delay as soon the first function from fnArr array viz. withDelay will run first. Only when this will complete its execution, the second function withoutDelay will start its execution. setTimeout will not guarantee your execution after provided interval, it is the minimum interval after which your function will execute. You can read more about setTimeout here
While loop doesnot delay a function you need to use setTimeout in your withDelay() and it working fine
var withDelay = function (a) {
setTimeout(() => {console.log(a+"I am with delay")},5000);
}
// some non blocking functionality
var withoutDelay = function(a) {
console.log(a+"I am with no delay");
}
var fnArr = [withDelay, withoutDelay]; //array of functions
var args = ["Hi,"]; // arbitrary params
for( var i=0; i < fnArr.length; i++) {
var fn = fnArr[i];
(function(f,arg) {
return setTimeout(function(){ return f.apply(f,arg) },0);
})(fn,args)
}
An example to call functions in a line after delay. As asked by questioner.
function func1(){
console.log("Function 1 is executed");
console.timeEnd('t');
}
function func2(){
console.log("Function 2 is executed");
console.timeEnd('t');
}
function func3(){
console.log("Function 3 is executed");
console.timeEnd('t');
}
let arrr = [
{func:func1,delay:2000},
{func:func2,delay:2000},
{func:func3,delay:3000},
]
async function callWithDelay(funcArr){
for(let func of funcArr){
//just to see time in console not necesarry
console.time('t');
//create a promise
let promise = new Promise((resolve,reject) => {
//'promise' will resolve after the function inside following code will end
setTimeout(()=>
{
resolve();
func.func();
},func.delay)
})
//The code will not proceed until the 'promise' is resolved(func is excecuted);
let x = await promise;
}
console.log("All the functions are excecuted");
}
callWithDelay(arrr);

JavaScript wait for async functions in while loop

I currently have 5 functions; each one uses setInterval to wait for an element to load and than clicks the element when it is available which leads to the next webpage. These functions also take place inside of a while loop. It is very important that these functions take place one after another and the while loop waits for all the functions to complete before looping again. Due to the functions being asynchronous the loop will run x times before any of the functions can even load.
Example of what I am trying to do:
function one () {
var checkForItem = setInterval(function () {
if ($('#element').length) {
$('#element').click();
clearInterval(checkForItem);
}
}, 100);
}
Imagine 5 of these functions (one, two, three, four, five), all with the same format using setInterval and the following while loop:
var x = 0, y = 10;
while (x < y){
one();
two();
three();
four();
five();
x++
}
How would I go about ensuring all the functions take place one after another before having the loop continue?
Note: I have tried using promises although due to the functions being async the loop still continues before the functions complete.
Use async/await syntax with the promises:
function delay(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
async function one() {
while (!$('#element').length) {
await delay(100);
}
$('#element').click();
}
async function main() {
for (var x = 0; x < 10; x++) {
await one();
await two();
await three();
await four();
await five();
}
}
Define the selectors in an array, and gradually iterate over the array in the interval, until the end of the array is reached:
const initialSelectorsToFind = ['#element1', '#element2', '#element3']; // add more as desired
const elementSelectorsToFind = Array.from({ length: 10 })
.reduce(arrSoFar => [...arrSoFar, ...initialSelectorsToFind], []);
let elementIndexToFind = 0;
function tryClick(){
const elementToFind = $(elementSelectorsToFind[elementIndexToFind]);
if (elementToFind.length) {
elementToFind.click();
elementIndexToFind++;
if (elementIndexToFind === elementSelectorsToFind.length) {
clearInterval(tryClickInterval);
}
}
}
const tryClickInterval = setInterval(tryClick, 100);
But if you're trying to trigger a function (such as something that clicks an element) when an element gets added to the DOM, it would be far better off to use something that triggers when the add occurs, such as a callback in the creator function, or MutationObserver
Try wrap promise with async await:
async function one (){
await (new Promise(function(reolve, reject){
var checkForItem = setInterval(function () {
if ($('#element').length) {
$('#element').click();
clearInterval(checkForItem);
resolve();
}
}, 100);
}));
}
//And then in your while loop:
while (x < y){
await one();
await two();
...
x++
}
note: your while loop must be wrapped in an async function also.

Create a function that no matter how many times invoked run only when first async call finishes?

suppose I've a function fetch(id).
I would be calling it arbitrarily at random times.
But I want every successive call to run only after previous call has finished.
say the async task takes 4 seconds, and i call fetch 3 times. then total time should be 12 seconds.
I could make a array and at every call set promise to go through next in line.
but what are some ways to accomplish this.
I think I got it
//First my example function which could be anything but should return promise which would be queued
function example(n) {
return new Promise((res, rej) => {
setTimeout(()=> {
console.log(n);
res();
}, 1000);
});
}
//now solution
function debounce(func) {
let p = Promise.resolve();
return function(x){
p = p.then(() => func(x));
}
}
//usage
d = debounce(example);
d(1);d(2);d(3);d(4);d(5);d(6);d(1);
You can chain Promises without an array, just store a pointer to the last Promise
// async payload function
// returns a promise
function f(x) {
console.log(`call f(${x})`);
return new Promise((resolve) => {
setTimeout(() => {
console.log(`resolve f(${x})`);
resolve();
}, 2000);
});
}
// wrapper to call function `f` sequentially
// stores pointer to the last Promise in closure
const g = (function(){
let lastPromise = Promise.resolve();
return function(arg){
lastPromise = lastPromise.then(() => f(arg));
}
})();
// generate random calls of function function `g`
for (let i = 0; i < 5; i++) {
setTimeout(() => g(i), Math.random() * 100);
}
I guess you can use async.io library.
Or, each time you want to call your function, push the function itself in an array. When the previous function has finished, check if there is still some functions to call in the array.

Categories