how setTimeout works in promise in javascript? - javascript

i have a question, i don't know how setTimeout works in promise, i have to timer:
const start = new Date().getTime();
function a() {
setTimeout(() => {
console.log('output', new Date().getTime() - start);
}, 3000);
}
setTimeout(a, 1000);
output: 4000
then i change my code like this:
const start = new Date().getTime();
const p13 = new Promise((resolve,reject) => {
setTimeout(() => {
reject(new Error('error'));
},3000);
});
const p14 = new Promise((resolve,reject) => {
const s = new Date().getTime();
setTimeout(() => {
resolve(p13);
},1000);
});
p14.then((value) => {
console.log(value, 'value');
}).catch((error) => {
const end = new Date().getTime();
console.log('output:', end - start);
});
output: 3000, i don't know why? it should output 4000. why 3000.

At t = 0
a promise p13 is created that starts timeout1 with 3s waiting time
a promise p14 is created that starts timeout2 with 1s waiting time
At t = 1000
timeout2 triggers the callback function, chaining p14 with promise p13
timeout1 has traversed 1000ms
At t = 3000
timeout1 triggers the callback function, rejects promise and output is calculated
Let us take another example
const start = new Date().getTime();
const p13 = new Promise((resolve,reject) => {
setTimeout(() => {
const end = new Date().getTime();
console.log('output:', end - start);
reject(new Error('error'));
},3000);
});
const p14 = new Promise((resolve,reject) => {
const s = new Date().getTime();
setTimeout(() => {
resolve(p13);
},1000);
});
setTimeout(function(){
p14.then((value) => {
console.log(value, 'value');
}).catch((error) => {
console.log('error');
});
}, 5000);
As you can see in the above example, the timeout of the promise p13 gets triggered even before the the callback of setTimeout with 5000 ms delay is executed.
Conclusion - When you create a promise object the state of promise object is in pending state, however, the actual functionality gets triggered.

The promises with your timeouts are asynchronous.
When you create p13, it directly starts its timeout, which is 3000.
It's not waiting for the 1000 from p14. (which is what you think)
With more details about your code expected result:
If you put any number less than 3000 in p14, it will resolve with 300x. If you put more than 4000, then it will be rejected. (although timeout is not so accurate so it's more like around 3000 / 4000)

When you create a new Promise, the function which you pass as the constructor's argument is executed immediately. So the p13 timeout will invoke the rejection after 3000 ms after the Promise was created - not 3000 ms after it was passed to the resolve function. If you want your Promises to run one after another, you can refactor your code to something like this:
const start = new Date().getTime();
const p13 = () => {
return new Promise((resolve,reject) => {
setTimeout(() => {
reject(new Error('error'));
},3000);
});
}
const p14 = new Promise((resolve,reject) => {
const s = new Date().getTime();
setTimeout(() => {
resolve(p13());
},1000);
});
p14.then((value) => {
console.log(value, 'value');
}).catch((error) => {
const end = new Date().getTime();
console.log('output:', end - start);
});
p13 is now a function that creates a Promise. That function is executed after 1000 ms, the new Promise is created and returned and another timeout is scheduled. The second timeout rejects the Promise after another 3000 ms, so effectively after 4000 ms after the start of the program.

here is how you'd make it 4000
make p13 a function
resolve(p13());
now you're not starting p13 until p14 "completes"
const start = new Date().getTime();
const p13 = () => new Promise((resolve,reject) => {
setTimeout(() => {
reject(new Error('error'));
},3000);
});
const p14 = new Promise((resolve,reject) => {
const s = new Date().getTime();
setTimeout(() => {
resolve(p13());
},1000);
});
p14.then((value) => {
console.log(value, 'value');
}).catch((error) => {
const end = new Date().getTime();
console.log('output:', end - start);
});

Related

Javascript : Call function and wait seconds then

I feel stupid because I do not find what I want to do...
It is in PURE Javascript.
I wan't to call a function, and stop it (or kill it, or whatever) next some seconds.
Here is my actual code :
function scrollThumbnails() {
const thumbnails = document.querySelectorAll('.thumbnail');
for (thumbnail of thumbnails) {
thumbnail.classList.add('active');
await verticalSlider(thumbnail);
resetSliderPosition(thumbnail);
thumbnail.classList.remove('active');
}
scrollThumbnails();
}
async function verticalSlider(element) {
const slider = element.querySelector('.vertical-carrousel');
const max = slider.offsetHeight - slider.parentElement.offsetHeight;
var toTop = 0;
while (toTop > -max) {
await new Promise((resolve) => setTimeout(resolve, 50));
toTop--;
slider.style.top = toTop + 'px';
}
await new Promise((resolve) => setTimeout(resolve, 1000));
}
function resetSliderPosition(element) {
const slider = element.querySelector('.vertical-carrousel');
slider.style.removeProperty('top');
}
So here, i want to call 'verticalSlider' in my loop, but if it is over than 45 seconds, I want to stop it and go on the next thumbnail.
Is it possible ? Do I miss something ?
Thanks by advanced :)
This is simple example without async and await with manual timeout counter implementation.
// Function, which accepts time out value in seconds (counting from 0)
const myFunct = (timeOutSec) => {
// Get current time
const ct = Date.now();
// Set counter for example function logic
let secCounter = ct - 1000;
// Start eternal loop
while(true) {
// Get in loop time
const ilt = Date.now();
// Compare current time and in loop time
// If current time + timeout sec < in loop time, break it
if(ct + timeOutSec * 1000 < ilt) break;
// Here do main function logic in loop
// for example, console log each second
if(ilt - secCounter > 1000) {
console.log(`time ${ilt}`);
secCounter += 1000;
}
}
}
// Test running function for 4 sec
myFunct(3);
console.log(`Completed at ${Date.now()}`);
Or similar with promises
// Async timeout function
const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Promise function which accepts timout value in seconds (counting from 0)
const myPFunct = (timeOutSec) => {
return new Promise(async (resolve, reject) => {
// Current time
const ct = Date.now();
// start eternal loop
while(true) {
// In loop time
const ilt = Date.now();
// Check in-loop time, current time and timeout
if(ct + timeOutSec * 1000 < ilt) {
resolve(`completed at ${ilt}`);
break;
}
// Do something
console.log(`time: ${ilt}`);
// Wait 1 sec
await timeout(1000);
}
});
}
// Test async function
const test = async () => {
// Run myPFunct for 4 sec
const result = await myPFunct(3);
console.log(`Test function result: ${result}`);
}
// Run test function
test();
Thanks to #tarkh solution.
If someone try to do an horizontal slider like me, or something with loop, I have to modify the code (because with the source solution, the resolve goes up to all the async methods).
Here is the final solution (I have to name the loop to break it and resolve the current promise - there are maybe simplest solution ?) :
window.onload = function () {
scrollThumbnails();
}
const scrollThumbnails = async () => {
const thumbnails = document.querySelectorAll('.thumbnail');
for (thumbnail of thumbnails) {
thumbnail.classList.add('active');
await verticalSliderWithTimeout(thumbnail, 45000);
thumbnail.classList.remove('active');
}
scrollThumbnails();
}
const timeout = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const verticalSliderWithTimeout = (element, timeoutDelay) => {
return new Promise(async (resolve, reject) => {
const currentTime = Date.now();
const slider = element.querySelector('.vertical-carrousel');
const max = slider.offsetHeight - slider.parentElement.offsetHeight;
var toTop = 0;
slider.style.top = toTop + 'px';
sliderLoop: while (toTop > -max) {
const inLoopTime = Date.now();
if(currentTime + timeoutDelay < inLoopTime) {
break sliderLoop;
}
await timeout(50);
toTop--;
slider.style.top = toTop + 'px';
}
resolve();
});
}
HTML code if needed (Handlebars) :
<div class="thumbnail">
<div class="photos-slider">
<div id="vertical-carrousel" class="vertical-carrousel">
{{#each this.photos}}
<img src="/image/{{../this.agency.product_type}}/{{../this.agency.id}}/{{../this.reference}}/{{urlencode this}}"
onerror="{{this}}" />
{{/each}}
</div>
</div>
</div>

What makes `async/await` statements run sequentially vs in parallel in ES6?

I have already gone through the thread Any difference between await Promise.all() and multiple await?, so I am clear about Promise.all and multiple awaits.
Still, I am not very clear about the below 2 scenarios.
In Case 1 why does it execute sequentially (takes 10s) whereas in Case 2 it executes in parallel (takes 4s)?
Case 1:
function promiseWait(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, time);
});
}
async function test1() {
var t0 = performance.now()
var a = await promiseWait(1000)
var b = await promiseWait(2000)
var c = await promiseWait(3000)
var d = await promiseWait(4000)
var t1 = performance.now()
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds."); //takes 10secs
}
test1()
Case 2:
function promiseWait(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, time);
});
}
async function test2() {
var t0 = performance.now()
const p1 = promiseWait(1000);
const p2 = promiseWait(2000);
const p3 = promiseWait(3000);
const p4 = promiseWait(4000);
const a = await p1;
const b = await p2;
const c = await p3;
const d = await p4;
var t1 = performance.now()
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")
}
test2()
In the first case because of the await before each call to the promiseWait, to even start executing the next call to the promiseWait it needs to wait until the first call to the promiseWait is finished completely. So you see sequential execution.
In the second case you have already invoked all the promiseWait functions before you start awaiting them. So the promiseWait has already started executing, then you are awaiting the results one after another.
Implementation wise in the first scenario, the next call to the setTimeout has to wait until the first setTimeout expires. So the second, third and the fourth timers need to wait until the first timer expires and resolves the promise in order to be scheduled.
In the seconds case you schedule the setTimeout calls one after the other, so the timers are all already queued. Then you are just awaiting for the timers to expire and resolve your promise one by one.
With some more deep dive ... (Inspired by Philip Roberts).
Case 1: SetTimeout triggers one by one
function promiseWait(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(new Date().toISOString().split('.')[0].replace('T',' '));
}, time);
});
}
async function test1() {
let timeStamp = new Date();
var t0 = new Date();
console.log("t0",t0)
var a = await promiseWait(1000)
console.log("a",a)
var b = await promiseWait(2000)
console.log("b",b)
var c = await promiseWait(3000)
console.log("c",c)
var d = await promiseWait(4000)
console.log("d",d)
var t1 = new Date();
console.log("t1",t1)
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds."); //takes 10secs
}
test1()
Console: You can notice that sum of each timeout is resulted ~10 Secs.
Case 2: SetTimeout triggers at same time.
function promiseWait(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(new Date().toISOString().split('.')[0].replace('T',' '));
}, time);
});
}
async function test1() {
let timeStamp = new Date();
var t0 = new Date();
console.log("t0",t0)
const p1 = promiseWait(1000);
const p2 = promiseWait(2000);
const p3 = promiseWait(3000);
const p4 = promiseWait(4000);
console.log("p1",p1);
console.log("p2",p2);
console.log("p3",p3);
console.log("p4",p4);
const a = await p1;
console.log("a",a);
const b = await p2;
console.log("b",b);
const c = await p3;
console.log("c",c);
const d = await p4;
console.log("d",d);
var t1 = new Date();
console.log("t1",t1)
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds."); //takes 10secs
}
test1()
Console: Here we can notice that timer started together so it resulted with ~ 4 Secs. (Last timer is set at 4 sec).
I this case all 4 promises started at the same time. Therefore its setTimeout timer runs parallel in the background.
The timers have an difference with 1 second so if you put an console log between the awaits you should see around every second a new console log.
function promiseWait(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
}, time);
});
}
async function test2() {
var t0 = performance.now()
console.log("putting promises in que");
const p1 = promiseWait(1000);
const p2 = promiseWait(2000);
const p3 = promiseWait(3000);
const p4 = promiseWait(4000);
console.log("promises where added to the que");
const a = await p1;
console.log("1")
const b = await p2;
console.log("2")
const c = await p3;
console.log("3")
const d = await p4;
console.log("4")
var t1 = performance.now()
console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")
}
test2()
Visualized it looks something like this:
setTimeout(() => {
let divs = document.querySelectorAll("div").forEach(div => {
div.style.setProperty("width", "100%")
})
},500)
div {
height: 30px;
background: red;
margin: 10px;
width: 0px;
transition: width 4s;
color: white
}
<div style="max-width: 100px";>
Promise 1
</div>
<div style="max-width: 200px";>
Promise 2
</div>
<div style="max-width: 300px";>
Promise 3
</div>
<div style="max-width: 400px";>
Promise 4
</div>
Case 1 visualized:
let divs = document.querySelectorAll("div")
let delay = (time) => new Promise(res => setTimeout(res, time));
(async() => {
for(let i = 0; i < divs.length; i++) {
await delay(i + 1000);
divs[i].style.setProperty("width", "100%");
}
})()
div {
height: 30px;
background: red;
margin: 10px;
width: 0px;
transition: width 4s;
color: white
}
<div style="max-width: 100px";>
Promise 1
</div>
<div style="max-width: 200px";>
Promise 2
</div>
<div style="max-width: 300px";>
Promise 3
</div>
<div style="max-width: 400px";>
Promise 4
</div>

Ignore the function call for a specific time

I am looking for an optimal solution for the described problem.
Scenario: There is a function getData() which is being called every second.
If it is called now I want to ignore any call to this function for let say 5 sec.
how best we can achieve this in javascript.
Save last call time and check if passed more than 5 seconds:
var lastCall = 0;
function getData() {
if (lastCall >= moment().subtract(5, 'mins').unix()) {
return;
}
lastCall = moment().unix();
/* rest of code */
}
Add a flag and toggle it after 5 seconds and on each not ignored call:
var shouldIgnore = false;
function getData() {
if (shouldIgnore) {
return;
}
shouldIgnore = true;
setTimeout(() => {
shouldIgnore = false;
}, 5000);
/* rest of code */
}
There are may may using setTimeout you can do. I have give sample example with some util mathod to make it simpler.
Throttle Function:
const throttle = (fn, ms = 0) => {
let lastRunTime;
return function(...args) {
const currTime = +new Date();
if (!lastRunTime || currTime - lastRunTime > ms) {
lastRunTime = +new Date();
fn.apply(this, args);
}
};
};
How to use it:
(async function throttleEx() {
const logTill1Sec = throttle(log, 1 * 1000);
logTill1Sec("deepakt_1");
await new Promise(r => setTimeout(r, 500)); //2 sec virtual delay
logTill1Sec("deepak_t2");
})();
Output:
Mr. deepakt_1
Here you notice, even I call logAfter5Sec multiple times. It execute last one. You can write same way call once.
const throttle = (fn, ms = 0) => {
let lastRunTime;
return function(...args) {
const currTime = +new Date();
if (!lastRunTime || currTime - lastRunTime > ms) {
lastRunTime = +new Date();
fn.apply(this, args);
}
};
};
(async function throttleEx() {
const logTill1Sec = throttle(log, 1 * 1000);
logTill1Sec("deepakt_1");
await new Promise(r => setTimeout(r, 500)); //2 sec virtual delay
logTill1Sec("deepak_t2");
})();
const debounce = (fn, ms = 0) => {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), ms);
};
};
const dLog = debounce(log, 200); //ms time
dLog("deepak11");
dLog("deepak22");
dLog("deepak33");
function log(name) {
console.log(`Mr. ${name}`);
}
(async function() {
const logAfter5Sec = debounce(log, 1 * 1000);
logAfter5Sec("deepak");
await new Promise(r => setTimeout(r, 500)); //2 sec virtual delay
logAfter5Sec("deepak2");
})();

How do I make a running total of a variable that changes in JavaScript due to live data?

I need to measure how long the following code calls the vibrate function in seconds and display the seconds which I have working. However, I don't know how to create a running total so that every time vibrate() is called, it both measures the length in seconds and then adds this to a running total.
function goodValue() {
startTime = new Date()
string1 = 'GOOD!!! :)'
displayValue('Angles', string1)
}
var startTime = {}
function badValue() {
var endTime = new Date()
vibrate1(1500)
string2 = 'BAD!!! :('
displayValue('Angles', string2)
var timeDiff = endTime - startTime //in ms
// strip the ms
timeDiff /= 1000
// get seconds
seconds = Math.round(timeDiff);
displayValue('TotalTime', + seconds);
}
This should now work. It's assuming that you call goodAngle() when the app recognizes a good angle, and badAngle() when the app recognizes a bad angle.
var totalTime = 0
var startTime = false
function goodAngle() {
if (startAngle) {
timeDiff = (Date.now() - startTime) / 1000
console.log('timeDiff: %s seconds', timeDiff)
totalTime += timeDiff
console.log('total timeDiff: %s seconds', timeDiff)
string1 = 'GOOD!!! :)'
displayValue('Angles', string1)
displayValue('BadPostureTime', Math.round(timeDiff))
startTime = false // So it can't be called again unless there's a badAngle first
}
}
function badAngle() {
if (!startTime) {
// Only set startTime if there isn't a current timer running
startTime = Date.now()
vibrate1(1500)
string2 = 'BAD!!! :('
displayValue('Angles', string2)
}
}
Skip to the expanded code snippet for the answer to this question. The rest of the examples address measuring other related metrics.
Measuring Each Individual Time
If you want to measure a function's execution time, you can wrap it in another function that uses console.time() and console.timeEnd():
function measure (fn, label = fn.name) {
return function () {
try {
console.time(label)
return fn.apply(this, arguments)
} finally {
console.timeEnd(label)
}
}
}
function delay (ms) {
const now = Date.now()
while (Date.now() - now < ms) { }
}
// wrap the function that needs to be tested
const timedDelay = measure(delay)
timedDelay(100)
timedDelay(10)
This measure() function has a few nice features:
The original function's return value is always preserved
The original function's thrown error is always preserved
If the original function throws an error, the time taken is still logged
The optional label argument is used to label the time that appears in the console
The label defaults to the name of the original function
The original function receives the context and arguments exactly as they were passed
To measure asynchronous functions, you can use the following, which can handle concurrent calls by incrementing a number used within the label in addition to the features above:
function measureAsync (fn, name = fn.name) {
let index = 0
return async function () {
const label = `${name} (${++index})`
try {
console.time(label)
return await fn.apply(this, arguments)
} finally {
console.timeEnd(label)
}
}
}
function delay (ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
// wrap the async function that needs to be tested
const timedDelay = measureAsync(delay)
timedDelay(1000)
timedDelay(100)
timedDelay(500)
Measuring Total Time
Using the above two implementations, you can modify them with the Performance API to measure cumulative time instead:
Synchronous
function measureTotal (fn, label = fn.name) {
return Object.assign(function measured () {
try {
performance.mark(label)
return fn.apply(this, arguments)
} finally {
performance.measure(label, label)
const [{ duration }] = performance.getEntriesByName(label, 'measure')
const total = measured.total += duration
performance.clearMarks(label)
performance.clearMeasures(label)
console.log(`${label}: ${total.toFixed(3)}ms`)
}
}, {
total: 0
})
}
function delay (ms) {
const now = Date.now()
while (Date.now() - now < ms) { }
}
// wrap the function that needs to be tested
const timedDelay = measureTotal(delay)
timedDelay(100)
timedDelay(10)
timedDelay(50)
console.log('total:', timedDelay.total)
Asynchronous
function measureTotalAsync (fn, name = fn.name) {
let index = 0
return Object.assign(async function measured () {
const label = `${name} (${++index})`
try {
performance.mark(label)
return await fn.apply(this, arguments)
} finally {
performance.measure(label, label)
const [{ duration }] = performance.getEntriesByName(label, 'measure')
const total = measured.total += duration
performance.clearMarks(label)
performance.clearMeasures(label)
console.log(`${label}: ${total.toFixed(3)}ms`)
}
}, {
total: 0
})
}
function delay (ms) {
return new Promise(resolve => {
setTimeout(resolve, ms)
})
}
// wrap the async function that needs to be tested
const timedDelay = measureTotalAsync(delay)
Promise.all([
timedDelay(1000),
timedDelay(100),
timedDelay(500)
]).finally(() => {
console.log('total:', timedDelay.total)
})
References
performance.mark()
performance.measure()
performance.getEntriesByName()
It is hard to tell what your code is supposed to do but I would advise making
a function which would wrap around your vibrate1 implementation.
The measureTime function takes a function as an argument (in your case vibrate) and
returns a function which has a property totalTime that records the execution
time. Note that it uses performance.now() instead of a Date object. See
https://developer.mozilla.org/fr/docs/Web/API/Performance/now for reference.
function measureTime(fn) {
const returnedFn = function measuredFunction (arg) {
const start = performance.now();
fn(arg);
const end = performance.now();
returnedFn.totalTime = returnedFn.totalTime + ((end - start) / 1000);
};
returnedFn.totalTime = 0;
return returnedFn;
}
const measuredVibrate = measureTime(vibrate1);
displayValue(measuredVibrate.totalTime);

setTimeout() being called immediately in throttle function

so I'm trying to write a throttle function that when called, calls a callback but only up to a certain limit number of times in a given interval. If the limit is reached, the callback gets pushed into a queue where the callback gets called after the initial interval.
const throttle = (cb, interval, maxCalls) => {
let calls = 0;
const records = [];
let totalCalls = 0;
return (...rest) => {
if(calls < maxCalls) {
calls++;
records.push(Date.now());
totalCalls++;
cb.apply(null, rest);
setTimeout(() => {
calls--;
}, interval);
} else {
//cb within setTimeout being invoked immediately here
setTimeout(() => {
calls++;
records.push(Date.now());
totalCalls++;
cb.apply(null, rest);
//console.log(allotedTime: interval - (Date.now() - records[(totalCalls-1)-maxCalls]));
}, interval - (Date.now() - records[(totalCalls-1)-maxCalls]));
}
}
}
const meow = (start, ...args) => {
console.log(Date.now() - start, ...args);
}
const burp = throttle(meow.bind(this, Date.now()), 10000, 2);
setTimeout(() => burp('burp'), 0); // expect 2-7 'burp'
setTimeout(() => burp('burp'), 5000); // expect 5000 'burp'
setTimeout(() => burp('burp'), 6000); // expect 10000 'burp'
setTimeout(() => burp('burp'), 7000); // expect 15000 'burp'
The main problem is that for some reason, within the else block, the function isn't waiting for the setTimeout and is being called immediately. The syntax seems fine so I'm having a hard time figuring out why it's being called. This is the output after being called:
setTimeout(() => burp('burp'), 0); //6 'burp'
setTimeout(() => burp('burp'), 5000); //5001 'burp'
setTimeout(() => burp('burp'), 6000) //6001 'burp'
//allotedTime: 4005
setTimeout(() => burp('burp'), 7000); //10008 'burp'
//allotedTime: 4993
You'll notice that if you add the allotedTime with the result from the line above, you'll get the desired log. Thanks for taking a look.
Link to repl
Not sure if I understood the problem entirely, but based on your expectations,
the else interval should be:
interval - (Date.now() - records[totalCalls-maxCalls]))
because totalCalls is increment once the callback is called. So, either add totalCalls++; as the first statement of the else block (before setInterval()) or don't expect the value to be incremented (suggestion number 1).

Categories

Resources