Interval synced to Time - javascript

I want to execute a function in an interval. Yeah I could use setInterval but I need the interval to be synced to the timestamp or something.
Like I want to execute the interval on two different devices and they should run in the exact same second or even ms if possible. But depending on when I star the script these intervals would be offset if I would use setInterval method.
I've already tried this but it kinda acts weird.
setInterval(() => {
if (new Date().getTime() % 1000 * 10 == 0) {
console.log(new Date().toLocaleTimeString())
}
}, 1);

Like I want to execute the interval on two different devices and they should run in the exact same second or even ms if possible.
There's no guarantee that you can do this, not least because the JavaScript thread on one of the devices may be busy doing something else at that precise moment (it could even be tied up for several seconds).
Other than that, there's the issue of synchronizing the devices. Options are:
Some kind of synchronization event you send simultaneously to both devices. You'd run your code in response to the synchronization event received from your server. This is naturally subject to network delays, it requires a server to send the event (probably over web sockets), and is subject to the above caveat about the JavaScript thread being busy.
Relying on the devices being synced to exactly the same time source (for instance, perhaps they're both using a NIST time server or similar). If you know their times are synchronized sufficiently for your purposes, you can schedule your timer to fire at a precise moment, like this:
// Fire at exactly 14:30 GMT on 2021-04-21
const target = new Date(Date.UTC(2021, 3, 21, 14, 30)); // 3 = April, starts with 0 = Jan
const delay = Date.now() - target;
if (delay < 0) {
// It's already later than that
} else {
setTimeout(() => {
// Your code here
}, delay);
}
BUT, again, if the JavaScript thread is busy at that precise moment, the timer callback will run later, when the thread is free.
The code above schedules a single event, but if you need a recurring one, you can do the same basic logic: Determine the date/time you want the next callback to occur, find out how many milliseconds it is between now and then (Date.now() - target), and schedule the callback for that many milliseconds later.

Related

Need Javascript help to avoid timeouts when loading audio files

I support several churches that don't have musicians, by providing a little website with a bunch of pure Javascript so they can select music for their services from a collection of about 1100 mp3 and m4a music files. Previously, they created playlists in iTunes or Media Player, but after a track completed, the player would immediately start the next track unless they quickly clicked 'Stop'. So my website allows them to select all their music ahead of time (up to 10 tracks), with a separate "Play" button for each. Hit "Play" and it plays that one track and stops. (Duh.)
I'm encountering delays in loading the files into my "audio" tags - and I need the file to load when they select it so I can display the track duration, which is frequently important to the selection of the music for the service. A delay doesn't occur very often, but often enough to be annoying. Also, the load will occasionally time out completely, even after several attempts. I've experimented played with various techniques, like using setTimeout with different values to allow several seconds before checking if it's loaded, or, loading 5 or 10 times with shorter timeout values until it's loaded. I created a test page that indicates that the timeouts vary greatly - from 2% to 5% of the time, to upwards of 25% occasionally (during tests of 1,000 to 10,000 random loads).
My first technique was relying on events (I tried both 'canplay' and 'canplaythrough' events with minimal difference):
const testAudio = document.getElementById('test-audio');
let timeStart = Date.now();
function loadMusic(p_file) {
testAudio.src = p_file;
testAudio.addEventListener('canplaythrough', musicLoaded);
timeStart = Date.now();
testAudio.load();
}
function musicLoaded() {
console.log('music loaded in ' + (Date.now()-timeStart) + 'ms');
testAudio.removeEventListener('canplaythrough', musicLoaded);
/* should I add/remove the listener each time I change the source file ? */
}
My second approach (from a post here: https://stackoverflow.com/questions/10235919/the-canplay-canplaythrough-events-for-an-html5-video-are-not-called-on-firefox) is to check the 'readyState' of the audio element after a specified timeout, rather than relying on an event. This question specifically addressed Firefox, so I should mention that in my tests Firefox has horrible load times for both the "events" and the "readyState" techniques. Chrome and Edge vary in the range of 2% to 6% load failure due to timeout and Firefox has 27% to 39% load timeouts.
let myTimeout = '';
function loadMusic(p_file) {
myTimeout = setTimeout(fileTimeout, 1000); /* I've tried various values here */
testAudio.src = p_file;
timeStart = Date.now();
testAudio.load();
}
function fileTimeout() {
if (testAudio.readyState > 3) {
console.log('music loaded in ' + (Date.now()-timeStart) + 'ms');
} else {
/* here, I've tried calling loadMusic again 5 to 10 times, which sometimes works */
/* or, just reporting that the load failed... */
console.log('music FAILED to load!');
}
}
I have a shared server hosting plan, and I suspect the delay might be due to traffic on my server. Unfortunately, my hosting service turns a deaf ear to anything that might be application or content related (not surprising). And this isn't worth upgrading to a dedicated server just to eliminate that variable. But I suspect that might be a major factor here.
I need a technique that will always work - even if it takes 30 seconds or more. As long as I can display an intermittent "Still loading..." type message I (and my users) would be satisfied. The "track X won't load" messages happen often enough to be annoying. Early on, I had a few files with bad characters in the file name that needed to be fixed before they would load. So the users think that problem persists. But I know I've fixed all them now.
Any and all suggestions are welcome - but I'd love to keep everything in plain Javascript.
Using an audio constructor:
function loadMusic(p_file) {
myTimeout = setTimeout(fileTimeout, 1000);
let audioConst = new Audio();
audioConst.src = p_file;
timeStart = Date.now();
}
function fileTimeout() {
if (audioConst.readyState > 3) {
console.log('music loaded in ' + (Date.now()-timeStart) + 'ms');
} else {
console.log('music FAILED to load!');
}
myTimeout = '';
}

Alarms in NodeJS (without any delay)

I am currently attempting to make an alarm system for a website that I control my sprinklers with.
Currently, I use sockets to get alarms from the client and store them in a JSON file in the server.
To "activate" the alarms, I have thought to have a setInterval function that checks whether the current time is included in the array (using .includes() )
I am very annooyed currently because the setInterval function is delays 65 milliseconds every time it is run
Does it wait for the code inside to finish before continuing? If so, how do I make it not do so
This is my code:
I think this may be relevant, if I could understand it (I am new)
console.log("Waiting for time to get in to sync...")
while (sync > 0 || sync == undefined) {
let date = new Date()
sync = date.getSeconds()
}
setInterval(()=>{
check(new Date()) //run the check code elsewhere
}, 60000); // one minute delay```

Javascript: Alternative to setTimeOut for FAST Timer in MIDI Sequencer App

I'm working on a Javascript Music App that includes a Sequencer. For those who are not familiar, MIDI sequencers work pretty much like this: There is something called PPQ: pulses per quarter note. Each pulse is called "Tick". It depicts how may "subdivisions" there are per quarter note, like resolution. So Sequencers "play" the Events that are in the tracks one Tick at a time: Play Tick1, wait Tick Duration, Play tick2, Tick Duration, and so on.
Now, let's say we have a BPM (Beats per Min) of 120 with PPQ=96 (standard). That means that each Quarter Note Duration is 500ms, and each Tick Duration is 5.20833ms.
What Timer Alternatives we have in Javascript?
1) We have the old setTimeOut. It has several problems: the min. wait time is 4ms. (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Minimum_delay_and_timeout_nesting)
It is also subject to JITTER/time Variations. It is not precise and it is demanding, as call backs are stacked in the even loop.
2) There is an alternative to setTimeOut/setInterval which involves using requestAnimationFrame(). It is VERY precise and CPU efficient. However, the minimum time it can be set is around 16.7ms (the duration of a Frame in a typical 60FPS monitor)
Is there any other Alternative? To to precisely schedule an event every 2-5ms?
Note: the function done in side the loop, playEventsAtTick() is NOT demanding at all, so it would never take more time to execute than Tick Duration.
Thanks!
Danny Bullo
To maintain any sanity in doing this kind of thing, you're going to want to do the audio processing on a devoted thread. Better yet, use the Web Audio API and let people who have been thinking about these problems for a long time do the hard work of sample-accuracy.
Also check out Web MIDI (chrome only).
Thanks nvioli. I'm aware of Web Audio API. However, I don't think that can help here.
I'm not triggering AUDIO directly: I have MIDI events (or let's say just "EVENTS") stored in the TRACKS. And those events happen at any TICK. So the Sequencer needs to loop every Tick Duration to scan what to play at that particular tick.
Regards,
Danny Bullo
In a separate thread, such as a web worker, you can create an endless loop. In this loop, all you need to do is calculate the time between beats. After the time is valid, you can then send a message to the main process, to do some visuals, play a sound or what ever you would like to do.
Here is a Working example
class MyWorker {
constructor() {
// Keeps the loop running
this.run = true
// Beats per minute
this.bpm = 120
// Time last beat was called
this.lastLoopTime = this.milliseconds
}
get milliseconds() {
return new Date().getTime()
}
start() {
while (this.run) {
// Get the current time
let now = this.milliseconds
// Get the elapsed time between now and the last beat
let updateLength = now - this.lastLoopTime
// If not enough time has passed restart from the beginning of the loop
if (updateLength < (1000 * 60) / this.bpm) continue;
// Enough time has passed update the last time
this.lastLoopTime = now
// Do any processing that you would like here
// Send a message back to the main thread
postMessage({ msg: 'beat', time: now })
}
}
}
new MyWorker().start()
Next we can create the index page, which will run the worker, and flash a square everytime a message comes back from the worker.
<!DOCTYPE html>
<html lang="en">
<head>
<script>
// Start the worker
var myWorker = new Worker('worker.js')
// Listen for messages from the worker
myWorker.onmessage = function (e) {
var msg = e.data
switch (msg.msg) {
// If the message is a `beat` message, flash the square
case 'beat':
let div = document.querySelector('div')
div.classList.add('red')
setTimeout(() => div.classList.remove('red'), 100)
break;
}
}
</script>
<style>
div { width: 100px; height: 100px; border: solid 1px; }
.red { background: red; }
</style>
</head>
<body>
<div></div>
</body>
</html>
Get Off My Lawn: The approach you suggested does not completely work. Let's say I add a method to the web worker to STOP the Sequencer:
stop() {
this.run = false;
}
The problem is that the method myWorker.onmessage = function (e) {...} never get's triggered. I suspect it is because the Web Worker Thread is "TOO BUSY" with the endless loop. any way to solve that?
Also, while playing, it works.....but the CPU goes up considerably..... The only possible Solution would be a Sleep() method, but Real SLEEP that does not exist in Javascript...
Thanks

Building an alarm clock style application

I am building a webapp (referred to as the "noticeboard") for a friend's business, to aid with their packaging and dispatch operation. It is built using HTML, CSS & JS. The backend is built in PHP / MYSQL.
The noticeboard is for the benefit of their staff and displays dispatch cut-off ("event") times, i.e as follows:
Dispatch Time 1 : 09:00
Dispatch Time 2 : 11:30
Dispatch Time 3 : 14:30
Dispatch Time 4 : 16:00
They update these times on a regular basis, as their schedule depends on their delivery firm's schedule. There is an AJAX request running every 15 mins which simply fetches the latest times (JSON format) from the database and updates the noticeboard. Although I could just simply implement an "auto browser refresh" every 15 minutes, I found this was a bit inconsistent and sometimes a "page cannot be found" error message would be displayed.
The noticeboard also displays a real-time clock. I have built this using moment.js.
The system runs 24/7 in a Chrome browser running on Windows 10. Nothing else is running on the machine.
At the moment the noticeboard simply displays these times. I need to take this one step further and make it function almost like an alarm clock. What I'm basically looking to achieve is 15 minutes before each event, it needs to highlight the upcoming event time (i.e. using jQuery addClass()). Then as soon as that event time is reached, play a buzzer sound (some kind of MP3 file). This needs to happen automatically every day for every event. Remember the event times are always changing, so it would need to be smart enough to recognise this.
What techniques can I use to achieve this functionality? I have been reading up on things like setTimeout() and setInterval(), however I'm not sure these are able to "auto-update" themselves once they have been set (i.e. if an event time changes). Do I need to look at a nodeJs based solution? I don't have any experience in nodeJs but if that's the best way to achieve this then I'm willing to give it a go. Otherwise I'm more than happy to try out something in vanilla JS.
Here's how I would approach it using setTimeout() but obviously this doesn't dynamically update:
// set the number of ms to 15 mins before the event time
var eventInterval = 36000000;
// setTimeout function for the event
setTimeout(function() {
// add "active" class to highlight the event
$('.event').addClass('active');
// after 15 mins have elapsed, remove the "active" class
setTimeout(function() {
$('.event').removeClass('active');
}, 90000);
}, eventInterval;
Your approach is fine, however, you need to do that EVERY TIME you get an AJAX response. setTimeout returns a timeoutId, which then you can use for cancelling the timeout with clearTimeout(timeoutId).
var reminderTime = 15 * 60 * 1000;
var timeoutIds = [];
function setTime(timestamp) {
// Set the interval 15 minutes before the event time.
var interval = timestamp - reminderTime;
var timeoutId = setTimeout(function() {
// add "active" class to highlight the event
$('.event').addClass('active');
// after 15 mins have elapsed, remove the "active" class
setTimeout(function() {
$('.event').removeClass('active');
}, 90000);
}, interval);
timeoutIds.push(timeoutId);
}
$.get("http://myserver/getTimes", function(times) {
// Reset all the setTimeouts
timeoutIds.forEach(function(timeoutId) {
clearTimeout(timeoutId);
});
// Assuming times is an array of timestamps
times.forEach(setTime);
});

Loop function for specified time, followed by delay

I have a small piece of code that continuously clicks a button called "See Older Messages" every 500 ms, in order to load infinitely-scrolled content from a webpage. Reasons for doing this are personal, but needless to say, I'm trying to automate something which would take me weeks of non-stop scrolling to do otherwise.
The problem is that the 500 ms delay gradually begins to drop as the script runs over time. After so many hours, it can take 5 seconds or more. I'm assuming this problem is caused by Facebook throttling my requests after so long, so to prevent this, I want to make the script run for an amount of time - say 2 minutes - followed by a delay of maybe 20 secs before it runs again for 2 mins, and so on. How would I go about doing this? I've racked my brains, but my limited knowledge of JavaScript hasn't come up with anything meaningful.
Below is the current code in its entirety.
setInterval(function () {
document.getElementById('see_older').getElementsByClassName('content')[0].click();
}, 500);
Thanks a lot in advance.
Keep track of when the script running started
While it's been less than 2 mins, keep clicking every 500ms.
After running for ~2 mins, stop and queue next run in 20s.
Go to step 2.
-
var lastChange;
function doClick() {
if (new Date() - lastChange < 120000 /* 2 mins */) {
document.getElementById('see_older').getElementsByClassName('content')[0].click();
setTimeout(doClick, 500);
} else setTimeout(runScript, 20000 /* 20s */);
}
(function runScript() {
lastChange = new Date();
doClick();
})();
-
I recommend using setTimeout over setInterval since, if the browser takes a while to execute, loses focus and stops executing JS, gets paged out, etc., then you will still get the time spacing between events that you want. See https://stackoverflow.com/a/731625/1059070.
Toggle whether or not your function does anything by setting another timer.
/* When true do load else don't. */
window.doLoad = true
setInterval(function () {
if window.doLoad {
document.getElementById('see_older').getElementsByClassName('content')[0].click();
}
}, 500);
/* This will toggle doLoad every two minutes. */
setInterval(function () {
if (window.onLoad == true) {
window.doLoad = false;
} else { window.doLoad = true; }
}, 120000); // two minutes of milliseconds
In your case though you might be better off using the Facebook Graph API.
Graph API documentation from Facebook
Here's an existing question with the API using Python to do basically the same thing you want to do.
JS question, also similar

Categories

Resources