How to return a Promise with setInterval() - javascript

I am trying to return a Promise object ever 1000ms, but i am not sure how to access the data returned in the Promise when it is inside a setInterval() callback.
EDIT
I appears i was not being very clear as to my intentions, so i will try and explain what it is i am trying to do. I making a count down where by the necessary calculations are are don every 1000ms based on specified end date.
Here is the code that provides the return value i would like returned as a Pormise value every 1000ms:
calculateTimeRemaining(endDate: string) {
const { secondsInDay, daysOfYear, secondsInHour, secondsInMinute } = this.unitsOfTime;
let distance: number =
(Date.parse(new Date(endDate).toString()) - Date.parse(new Date().toString())) / this.increment;
if (distance > 0) {
// Years left
if (distance >= daysOfYear * secondsInDay) {
// 365.25 * 24 * 60 * 60
this.timeRemaining.years = Math.floor(distance / (daysOfYear * secondsInDay));
distance -= this.timeRemaining.years * daysOfYear * secondsInDay;
}
// Days left
if (distance >= secondsInDay) {
// 24 * 60 * 60
this.timeRemaining.days = Math.floor(distance / secondsInDay);
distance -= this.timeRemaining.days * secondsInDay;
}
// Hours left
if (distance >= secondsInHour) {
// 60 * 60
this.timeRemaining.hours = Math.floor(distance / secondsInHour);
distance -= this.timeRemaining.hours * secondsInHour;
}
// Minutes left
if (distance >= secondsInMinute) {
// 60
this.timeRemaining.minutes = Math.floor(distance / secondsInMinute);
distance -= this.timeRemaining.minutes * secondsInMinute;
}
// Seconds left
this.timeRemaining.seconds = distance;
}
return this.timeRemaining;
}
Example:
const interval = window.setInterval(() => {
return new Promise((resolve, reject) => {
resolve('Hello');
});
}, 1000);
How to i access the Promise object with .then() afterwards?
Does not work:
interval.then((data) => console.log(data);

What you are looking for is an Observable, not a Promise. With promises, the callback you pass to then is executed at most once, so this:
interval.then((data) => console.log(data));
...will never print "Hello" more than once, even if you corrected the following mistakes in your code:
Whatever you return in a setInterval callback function is ignored.
setInterval does not return a promise, but an integer number, uniquely identifying the interval timer that was created.
On the other hand, an Observable can emit multiple events, contrary to a Promise.
There is an Observable proposal for EcmaScript, but you could create your own -- very simplified -- version of it:
class Observable {
constructor(exec) {
this.listeners = new Set;
exec({
next: (value) => this.listeners.forEach(({next}) => next && next(value)),
error: (err) => this.listeners.forEach(({error}) => error && error(err)),
complete: () => this.listeners.forEach(({complete}) => complete && complete())
});
}
subscribe(listeners) {
this.listeners.add(listeners);
return { unsubscribe: () => this.listeners.delete(listeners) }
}
}
// Create an Observable instead of a Promise;
const interval = new Observable(({next}) => {
setInterval(() => next("Hello"), 1000);
});
// Subscribe to that Observable
const subscription = interval.subscribe({ next: (data) => console.log(data) });
// Optionally use the returned subscription object to stop listening:
document.querySelector("button").addEventListener("click", subscription.unsubscribe);
<button>Stop listening</button>
Note that several JavaScript frameworks have an implementation of Observable.

Depending on what you're actually trying to do, an async iterable might do the job.
The difference is that an async iterable will only generate the next promise if you consume the last one. Intervals in JavaScript are tricky, even without promises. They try to run their callback at regular intervals, but the execution of any callback may be delayed if the interpreter is busy. That delay will not propagate, though. Also, short intervals will be throttled for background tabs.
Assuming your code is always waiting to consume the async iterable (e.g. in a for…of loop), you could do this:
function delay(t) {
return new Promise(resolve => setTimeout(resolve, t))
}
async function *interval(t) {
while(true) {
let now = Date.now()
yield "hello"
await delay(now - Date.now() + t)
}
}
for await(const greeting of interval(1000)) console.log(greeting)

For interval you can define the interval function like this
function interval() {
return new Promise(function(resolve, reject) {
setInterval(function() {
resolve('Hello');
}, 1000)
})
};
For interval you can use this:
way 1:
interval().then((x) => {
console.log(x);
})
way 2:
const intervalId = setInterval(() => {
interval().then((x) => {
console.log(x);
}, 1000)
})
This is just for stop the interval function after some time.
you must clear the interval if you does not need it more.
setTimeout(() => {
clearInterval(intervalId);
}, 10000);

I'm not sure if this will help but; Any function can be made into a promise and the alternative syntax [async keyword] might be useful for you in this case.
async function test() {
return "hello";
}
test().then( returned => console.log(returned)) // logs hello
setInterval() however does not return a return value rather it returns a "handle".
handle = window . setInterval( handler [, timeout [, arguments ] ] )
...
https://www.w3.org/TR/2011/WD-html5-author-20110705/spec.html#timers
You can, however, make promises from an setinterval ...
interval = window.setInterval(makepromise,1000)
async function makepromise() {
console.log("hello");
}
// or
interval = window.setInterval(async function () {console.log("hello");},1000)
But there is no place for a then then. We are back to callbacks, which we were trying to avoid! But there is functionality perhaps that we can use await within this function.
Better to make your calculateTimeRemaining to a promise And then you can use the then on the interval.
interval = window.setInterval(gameloop,1000);
function gameloop(endDate: string) {
calculateTimeRemaining(endDate: string).then(
//
// my then code goes here.
//
)
}
async calculateTimeRemaining(endDate: string) {
const { secondsInDay, daysOfYear, secondsInHour, secondsInMinute } = this.unitsOfTime;
let distance: number =
(Date.parse(new Date(endDate).toString()) - Date.parse(new Date().toString())) / this.increment;
if (distance > 0) {
// Years left
if (distance >= daysOfYear * secondsInDay) {
// 365.25 * 24 * 60 * 60
this.timeRemaining.years = Math.floor(distance / (daysOfYear * secondsInDay));
distance -= this.timeRemaining.years * daysOfYear * secondsInDay;
}
// Days left
if (distance >= secondsInDay) {
// 24 * 60 * 60
this.timeRemaining.days = Math.floor(distance / secondsInDay);
distance -= this.timeRemaining.days * secondsInDay;
}
// Hours left
if (distance >= secondsInHour) {
// 60 * 60
this.timeRemaining.hours = Math.floor(distance / secondsInHour);
distance -= this.timeRemaining.hours * secondsInHour;
}
// Minutes left
if (distance >= secondsInMinute) {
// 60
this.timeRemaining.minutes = Math.floor(distance / secondsInMinute);
distance -= this.timeRemaining.minutes * secondsInMinute;
}
// Seconds left
this.timeRemaining.seconds = distance;
}
return this.timeRemaining;
}
However, the value of promises is to avoid callback hell with an excessively complex call back scheme ... where the code is calling back, from call backs, from call backs, etc, etc, etc.
Promises do not operate in a 2nd operating system thread like a webworker. So unless you are attempting to clean up callbacks to make code readable or are actually waiting for something there is no benefit to make use of promises.
setInterval is a clean callback. The Gameloop example is not easier to read and understand because a promise was used. I would suggest in this case it is harder to read. at this point ... unless there are other awaits within the loop or a series of promises that do not need to run synchronously;

As already mentioned in the comments that you can not return promises on intervals, but you can keep them in a global object and use later,
const jobs = []
const interval = setInterval(() => {
if(jobs.length == 10) {
clearInterval(interval);
}
let job = Promise.resolve('new job created');
jobs.push(job);
console.log('job created')
}, 1000);
setTimeout(() => {
Promise.all(jobs).then(data => console.log(data))
}, 1000*15);

setInterval already return an integer which is useful to cancel this interval, using clearInterval.
const promise = new Promise((resolve, reject) => {
resolve('Hello');
});
Then use it like
promise.then((result) => {
console.log(result) // Says 'Hello' and will not resolve another value if we call it as it has already been resolved
})
Maybe this is what you tried to achieve.
If you want to call it with an interval of 1000 ms.
const getPromiseInstance = () => new Promise((resolve, reject) => {
resolve(Math.random());
});
setInterval(() => {
getPromiseInstance().then((result) => {
console.log(result)
})
}, 1000)
You should take a look to Observable, maybe it will fit your needs

Related

In React i'm getting this.setState is not a function error within setTimeout/setInterval

I've seen a few topics on the issue 'this.setState is not a function', but none have fixed it for me. I've used arrow functions or binding 'this', but still getting the error. The initial attempt is below. It uses nested setTimeout functions (i can explain why if it would help), but the issue is around this.setState.
Without looking into the 'useEffect' Hook, could i check whether i'm missing something basic/fundamental to setting state in React in the code below?
let delay = 5000;
var startTime = new Date().getTime();
let intervalTimerId = setTimeout(function request() {
// first apply a random number to a div
let randomNum = Math.floor(Math.random() * 6) + 1;
// document.querySelectorAll(".someClass"+randomNum)[0].setAttribute("id", "test");
// then after a second, remove the class
setTimeout(() => {
// document.querySelectorAll(".someClass"+randomNum)[0].setAttribute("id", "");
// after another second, set state to use later
setTimeout(() => {
this.setState({someState: "testing"});
}, 1000)
}, 1000)
// set new delay time
if (new Date().getTime() - startTime > 9000) {
delay = 3000;
}
console.log(new Date().getTime() - startTime)
intervalTimerId = setTimeout(request, delay);
}, delay);
You can try to use arrow function
let delay = 5000;
var startTime = new Date().getTime();
const request = () => {
// first apply a random number to a div
let randomNum = Math.floor(Math.random() * 6) + 1;
// document.querySelectorAll(".someClass"+randomNum)[0].setAttribute("id", "test");
// then after a second, remove the class
setTimeout(() => {
// document.querySelectorAll(".someClass"+randomNum)[0].setAttribute("id", "");
// after another second, set state to use later
setTimeout(() => {
this.setState({ someState: 'testing' });
}, 1000);
}, 1000);
// set new delay time
if (new Date().getTime() - startTime > 9000) {
delay = 3000;
}
console.log(new Date().getTime() - startTime);
intervalTimerId = setTimeout(request, delay);
};
let intervalTimerId = setTimeout(request, delay);
You can try to use bind:
let intervalTimerId = setTimeout(function request() { ... }.bind(this)
Assuming that you are using a class component and that this is not already broken, one of the motivations not to use class components:
In addition to making code reuse and code organization more difficult, we’ve found that classes can be a large barrier to learning React. You have to understand how this works in JavaScript, which is very different from how it works in most languages.
You can either bind this explicitly with .bind(this) or simply replace function syntax with the arrow function, as this within arrow function is equal to the outer this where the function is declared:
let intervalTimerId = setTimeout(() => {
...
setTimeout(() => {
this.setState({someState: "testing"});
}, 1000)
}, delay);

I encountered the problem that the execution result of javascript is not as expected

Code is as follows:
/**
* this is calculate Timer
* #param delay Timing steps
* #returns object Provide calling interface
*/
function countTime(delay = 20) {
let millseconds = 0,
seconds = 0,
timer = null;
// calculate
function calculate() {
millseconds += delay;
if (millseconds === 1000) {
seconds += 1;
millseconds = 0;
}
}
// start the calculate
function start() {
timer = setInterval(calculate, delay);
}
// end the calculate
function end() {
clearInterval(timer);
timer = null;
}
return {
start,
end,
seconds
};
}
const obj = countTime();
obj.start();
// Wait for a while to execute
obj.end();
// execute output
console.log(obj.seconds); // always output 0
Has anyone encountered a similar issue?
I really want to know what mechanism is causing javascript, Or is there something missing in my own writing?
There are 2 problems with the code. First one is that when your function gets executed it returns object with "seconds" property. Unfortunately, that property is not being calculated each time you check against it. So basically it's a stale value. To fix that you can create a new function in your code `getSeconds:
function countTime(delay = 20) {
function getSeconds() {
return seconds;
}
...
return {
start,
end,
getSeconds
}
Another issue is that you are not waiting enough time before checking for the seconds. So you should have something like that:
const obj = countTime();
obj.start();
setTimeout(() => {
obj.end();
console.log(obj.getSeconds());
}, 3000)

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

How to handle async/await calls inside a setTimeout() block?

I have a process that runs at a specific interval, let's say it runs on the every 10th second of the minute. It is a recursive function so at the end of every 10 seconds, it will call itself. This is working so far.
Inside this timer function, I have an async/await call to an API. Basically what it does is, it makes the API call, gets the data and that data is pushed to a client once it is %100 complete.
The problem is that if that async/await call takes longer than the interval of my timer, I am starting to get overlaps.
So, this is a bit of a design question. I have found the solution by using a global variable inside my API call wrapper function.
var REQUEST_CYCLE_ACTIVE = false;
or
var REQUEST_CYCLE_ACTIVE = true;
Inside the timer, I can skip the recursion if the API call is unfinished and it will try on the next interval.
This works. But I am curios to know if there is a more elegant way to solve this. I am also providing an example node.js application which demonstrates the issue and the solution.
// A simple function to simulate resource delays.
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
// Construct a time string for debug purposes.
const getTime = () => {
var now = new Date();
var h = now.getHours().toString().padStart(2, '0');
var m = now.getMinutes().toString().padStart(2, '0');
var s = now.getSeconds().toString().padStart(2, '0');
const timeSignature = h + ':' + m + ':' + s;
return timeSignature;
}
// Update
// This is the request wrapper.
const update = async () => {
REQUEST_CYCLE_ACTIVE = true;
let result;
try {
result = await someFakeAPI();
} catch (err) {
throw err;
}
let timeSignature = getTime();
console.log(`\t\t(REQUEST) update() is done. Here is the result:`);
console.log('\t\t', result)
// Once the data is %100 collected here (there are retries etc.)
// we make a call to the websocket and force-push the data to all
// of the connected clients.
// PUSH TO CLIENTS()
REQUEST_CYCLE_ACTIVE = false;
};
// The core mock API call function.
async function someFakeAPI() {
// Emulate an asynchroneous fetch.
console.log('\t\t(REQUEST) Fetching data ...')
// 12000 miliseconds (12 sec) is longer than our timer call.
// There will be an overlap.
await delay(12000);
let result = 0.6; // Change to 0.4 to trigger a failed fetch.
if (result < 0.5) {;
return '__FAIL__';
} else {
return {name: 'apple', price: '1234.12', time: 1549926859970};
}
}
// Just an initial API call to get things started.
async function sendExchangeRequest()
{
let result;
try {
result = await someFakeAPI();
} catch (err) {
throw err;
}
return result;
}
// runClock {{{1
const runClock2 = async () => {
let next_update_seconds;
// This is our interval, the recursive function will be called on every n'th second of the minute.
// For example, an interval of 10 means, the calls will be placed at 22:11:00, 22:11:10, 22:11:20 etc.
var interval = 10; //seconds
var date = new Date();
let current_seconds = date.getSeconds();
let buffer = 60 % interval;
let border = (60 - buffer);
// Interval calculations.
if (current_seconds <= border) {
if ((current_seconds % interval) == 0) {
next_update_seconds = interval;
} else {
let distance = (border - current_seconds);
let slice = (distance % interval);
next_update_seconds = slice;
}
} else {
next_update_seconds = (60 - current_seconds);
}
let next_update_milliseconds = (next_update_seconds * 1000);
var timeToNextTick = (next_update_milliseconds);
let timeSignature = getTime();
console.log(`[4] <${timeSignature}> Starting runClock(), time to next Async REQUEST: ${timeToNextTick}ms`);
setTimeout(function() {
let timeSignature = getTime();
console.log(`\t(${timeSignature}) Time is up, done waiting. Async REQUEST will be called.`);
if(REQUEST_CYCLE_ACTIVE){
console.log(`\t(${timeSignature}) Previous Async REQUEST already running. Skipping ...`);
}else{
console.log(`\t(${timeSignature}) Previous Async REQUEST is complete. Running a new one ...`);
// MAKE THE ASYNC API CALL HERE.
update();
}
// Recursion
console.log(`\t(${timeSignature}) Start runClock() again.`);
runClock2();
}, timeToNextTick);
};
var REQUEST_CYCLE_ACTIVE = false;
(async () => {
console.log('[1] Make the request:')
try {
response = await sendExchangeRequest();
console.log('[2] Report the result:\n\t', response)
console.log('[3] Start the clock cycle.');
runClock2();
} catch(error) {
console.log('FAILED:', error)
}
})();
If you have issues with the included code, here is the REPL link: AsyncConcepts2.repl.run
Finally, a gif demonstrating the example:

Changing the interval time when one of the states' value is changed

someFunction() {
//........ some stuff
else {
const getData = setInterval(() => {
this.sendRequest(id);
if (this.state.calls === this.state.selectedItems.length) {
this.setState({ done: true });
clearInterval(getData);
}
}, this.changeInterval());
}
},
changeInterval() {
if(this.state.processed === true) {
return 1000;
} else {
return parseInt(this.state.userSetValue, 10) * 1000 + 1000;
}
},
sendRequest(){
//........ some stuff
let {calls, processed} = this.state;
if(!processed){
processed = true;
}
calls++;
this.setState({calls, processed});
//........ some stuff
}
As i understand, the setInverval() function doesn't check its time interval whether it has changed or not after running function this.sendRequest(id).
My problem is that I have to run sendRequest() after waiting some time, and that some time can be changed dynamically in my app. Currently, it only runs in an interval when it's FIRST set.
How could I change the interval of setInterval() each time this.sendRequest(id) is executed ?
UPDATE
I've made some changes to someFunction():
someFunction() {
//........ some stuff
else {
let delay = parseInt(this.state.userSetValue, 10) * 1000 + 1000;
let runFunction = () => {
if (this.state.processed) {
delay = 1000;
}
// if (this.state.calls === this.state.selectedItems.length) {
// this.setState({ done: true });
// clearTimeout(runFunction);
// }
this.sendRequest();
setTimeout(runFunction, delay);
}
setTimeout(runFunction, delay);
}
}
The problem with this now is:
1) The code keeps running infinitely without stopping (the commented out if statement didn't make it stop)
2) I'd like the very first call to this.sendRequest() get executed after parseInt(this.state.userSetValue, 10) * 1000 + 1000 time and the rest consecutive calls executed after 1000 ms.
I have to call the this.sendRequest() this.state.selectedItems.length number of times. The very first call always has a delay ofparseInt(this.state.userSetValue, 10) * 1000 + 1000 ms while the rest have 1000 ms.
Could anyone show an example how to change the timer interval after the first calling some function ?
One of way of doing it would be to use setTimeout() instead of setInterval(). The basic ideas is as follows:
function doWork() {
// Do your work here
// Schedule more work with a new delay
var newDelay = 2000;
doWorkAfterDelay(newDelay)
}
function doWorkAfterDelay(delay) {
setInterval(doWork, delay)
}
var intialDelay = 1000;
doWorkAfterDelay(intialDelay);

Categories

Resources