setInterval() with fresh database data? - node.js

Hello and thank you in advance for taking your time to help me,
I've a setInterval() script who change some data in my MongoDB.
For example:
const isOn = mySavedDatabase.isOn;
var timer = setInterval(function() {
fetch('https://thewebsite.com', options)
.then(res => {
return res.json();
}).then(async data => {
if(data.data[0] && isOn == false) {
await xx.updateMe(xxx.id, { "isOn": true });
// some others actions here
} else if(isOn && !data.data[0]) {
await xx.updateMe(xxx.id, { "isOn": false });
} else if(isOn && data.data[0]) {
// nothing to do here.
}
})
}, 60000);
This script works fine, but it does not take the new changed data from the database.
If in the database, when the script starts, the value of "isOn" is true, it will remain true until the end of the script even if, in this one, I changed the data.
I checked, the value is changed by the setInterval() but, as long as it is in the loop, it does not take data from the database that it itself has changed.
Currently, for setInterval() work, I have to add each time :
clearInterval(timer);
But, it does not respect the time of one minute that I put and it sends way more requests than 1 per minute.

You will need to re-evaluate mySavedDatabase.isOn
I would just remove constant isOn, and use mySavedDatabase.isOn instaed.

Resolved.
Just pass the:
const isOn = mySavedDatabase.isOn;
In the setInterval() to get fresh data.

Related

reload API call at intervals based on the current path via svelte-pathfinder

I'm using svelte-pathfinder to observe URL parameters that are then sent to a method which runs an async API call. I'd like to be able to re-run my API call based on the current path at an interval (for example, every 50 seconds).
stores.js (note that fetchContests is a method that runs a await fetch() call to an external API based on the pattern and query values from svelte-pathfinder:
// routing
export const resultStore = derived([pattern, query], ([$pattern, $query], set) => {
if ($pattern('/search/') && $query.params.q) {
new Promise((resolve) => {
setTimeout(() => {
fetchContests('title', $query.params.q, true)
.then(set);
resolve()
}, delay)
})
} else if ($pattern('/')) {
new Promise((resolve) => {
setTimeout(() => {
fetchContests('contest_ids', dashboard, true)
.then(set);
resolve({name: "testing"})
}, delay)
})
}
}, []);
Results.svelte (where I display the results of the API call):
<script>
// data
export let promise;
import { resultStore } from './../stores.js';
// layout components
import Contest from "./Contest.svelte";
</script>
{#await promise}
<p>Loading contests</p>
{:then}
<ul>
{#each $resultStore as contest}
<Contest contest="{contest}"/>
{/each}
</ul>
{:catch error}
<p>Something went wrong: {error.message}</p>
{/await}
This works really well in a non-repetitive way:
On initial page load, it runs an API call based on the current path
Whenever the path changes (a link click or a form submission or whatever), it runs another API call with those parameters. Each call gets the appropriate data from the API.
What I haven't been able to do is add setInterval to this in a way that preserves the path. So if the path is http://localhost:8080/#!/search?q=governor and 50 seconds have elapsed, the interval would make an API call with those current parameters to retrieve updated data. If the path is http://localhost:8080/, or http://localhost:8080/#!/search?q=senator instead when the next 50 seconds have elapsed, it would do the same with those parameters.
Originally, I thought maybe I could add setInterval to the API call itself, which is an async function that runs await fetch. But this didn't work, it would run multiple API calls ignoring the current path and parameter settings. I thought maybe I could run it within resultStore instead (above), but then it only ran after 50 seconds instead of running on initial page load and then again after each 50 second interval.
When extracting the logic that should run every time pattern or query changes and after a given interval after that, I think this might match what you want to do?
let delay = 50000
// routing
export const resultStore = derived([pattern, query], ([$pattern, $query], set) => {
fetchAndSet($pattern, $query, set)
const interval = setInterval(() => {
fetchAndSet($pattern, $query, set)
}, delay);
// If you return a function from the callback, it will be called when
// a) the callback runs again, or b) the last subscriber unsubscribes.
return () => {
clearInterval(interval);
};
}, []);
function fetchAndSet($pattern, $query, set) {
if ($pattern('/search/') && $query.params.q) {
fetchContests('title', $query.params.q, true).then(set)
} else if ($pattern('/')) {
fetchContests('contest_ids', dashboard, true).then(set)
}
}

Does cloud function that writing to the same document trigger itself again

For example I have this cloud function
export const testFunction = functions.firestore
.document('posts/{postID}')
.onWrite((change) => {
if (!change.after.data()) return;
const { count = 0 } = change.after.data();
t.set(change.after.ref, { count: count + 1 }, { merge: true });
});
Will it be stuck in a loop since it's constantly updating the document field and triggering itself?
Your code does not show what t is, so we can't tell exactly what this function is doing.
But if you are updating a document within a trigger that fired in response to an update on that same document, the trigger will execute again. It's up to you to make the function return without updating the document in order to avoid an infinite loop. So, you will need to figure out how to determine if that's the case.

Compare API response against itself

I am trying to:
Poll a public API every 5 seconds
Store the resulting JSON in a variable
Store the next query to this same API in a second variable
Compare the first variable to the second
Print the second variable if it is different from the first
Else: Print the phrase: 'The objects are the same' if they haven't changed
Unfortunately, the comparison part appears to fail. I am realizing that this implementation is probably lacking the appropriate variable scoping but I can't put my finger on it. Any advice would be highly appreciated.
data: {
chatters: {
viewers: {
},
},
},
};
//prints out pretty JSON
function prettyJSON(obj) {
console.log(JSON.stringify(obj, null, 2));
}
// Gets Users from Twitch API endpoint via axios request
const getUsers = async () => {
try {
return await axios.get("http://tmi.twitch.tv/group/user/sixteenbitninja/chatters");
} catch (error) {
console.error(error);
}
};
//Intended to display
const displayViewers = async (previousResponse) => {
const usersInChannel = await getUsers();
if (usersInChannel.data.chatters.viewers === previousResponse){
console.log("The objects are the same");
} else {
if (usersInChannel.data.chatters) {
prettyJSON(usersInChannel.data.chatters.viewers);
const previousResponse = usersInChannel.data.chatters.viewers;
console.log(previousResponse);
intervalFunction(previousResponse);
}
}
};
// polls display function every 5 seconds
const interval = setInterval(function () {
// Calls Display Function
displayViewers()
}, 5000);```
The issue is that you are using equality operator === on objects. two objects are equal if they have the same reference. While you want to know if they are identical. Check this:
console.log({} === {})
For your usecase you might want to store stringified version of the previousResponse and compare it with stringified version of the new object (usersInChannel.data.chatters.viewers) like:
console.log(JSON.stringify({}) === JSON.stringify({}))
Note: There can be issues with this approach too, if the order of property changes in the response. In which case, you'd have to check individual properties within the response objects.
May be you can use npm packages like following
https://www.npmjs.com/package/#radarlabs/api-diff

clearInterval on "OFF" payload

I am having newbie difficulties implementing clearInterval with a certain payload.
The
if(msg.payload.state === "OFF")
works, but the timer doesn't stop.
Amongst many resources, I have seen how to stop setInterval in JS in node-red..?, node.js: how to use setInterval and clearInterval?, clearInterval doesn't clearInterval & clearInterval On Image Slider Plugin
I did have a similar function, "dominos" that I used to make sure I was parsing the "OFF" payload.
I have included a lot of commented code to show what I had tried. Sorry about the mess!
Is it an issue with my syntax?
Is the code logical?
var input = msg.payload.state;
let timer = 0;
var red = {"state":"ON","brightness":255,"color":{"r":255,"g":0,"b":0}};
var green = {"state":"ON","brightness":255,"color":{"r":0,"g":255,"b":0}};
function xmas() { // REPEATS!
node.send({payload:red});
setTimeout(function(){
node.send({payload:green});
}, 1500);
}
// repeat with 3 second interval
timer = setInterval(() => xmas(), 3000);
if (msg.payload.state === "OFF") {
timer = clearInterval();
}
The important thing to remember about a function node is that it's state is totally reset for each incoming message. This means that timer will ALWAYS be reset to zero by the let timer = 0;
If you want to store any state between messages then you need to use the context.
Also the way you are using clearInterval() will never work, you need to pass the reference to the timer object to this function for it to do anything useful.
The following function node should do what I think you intended.
var red = {"state":"ON","brightness":255,"color":{"r":255,"g":0,"b":0}};
var green = {"state":"ON","brightness":255,"color":{"r":0,"g":255,"b":0}};
if (msg.payload.state != "OFF") {
var timer = setInteval(()=>{
node.send({payload: red});
setTimeout(() => {
node.send({payload: green});
}, 1500);
}, 3000);
context.set("timer",timer);
} else {
var timer = context.get("timer");
clearInterval(timer);
}

How to run asynchronous tasks synchronous?

I'm developing an app with the following node.js stack: Express/Socket.IO + React. In React I have DataTables, wherein you can search and with every keystroke the data gets dynamically updated! :)
I use Socket.IO for data-fetching, so on every keystroke the client socket emits some parameters and the server calls then the callback to return data. This works like a charm, but it is not garanteed that the returned data comes back in the same order as the client sent it.
To simulate: So when I type in 'a', the server responds with this same 'a' and so for every character.
I found the async module for node.js and tried to use the queue to return tasks in the same order it received it. For simplicity I delayed the second incoming task with setTimeout to simulate a slow performing database-query:
Declaration:
const async = require('async');
var queue = async.queue(function(task, callback) {
if(task.count == 1) {
setTimeout(function() {
callback();
}, 3000);
} else {
callback();
}
}, 10);
Usage:
socket.on('result', function(data, fn) {
var filter = data.filter;
if(filter.length === 1) { // TEST SYNCHRONOUSLY
queue.push({name: filter, count: 1}, function(err) {
fn(filter);
// console.log('finished processing slow');
});
} else {
// add some items to the queue
queue.push({name: filter, count: filter.length}, function(err) {
fn(data.filter);
// console.log('finished processing fast');
});
}
});
But the way I receive it in the client console, when I search for abc is as follows:
ab -> abc -> a(after 3 sec)
I want it to return it like this: a(after 3sec) -> ab -> abc
My thought is that the queue runs the setTimeout and then goes further and eventually the setTimeout gets fired somewhere on the event loop later on. This resulting in returning later search filters earlier then the slow performing one.
How can i solve this problem?
First a few comments, which might help clear up your understanding of async calls:
Using "timeout" to try and align async calls is a bad idea, that is not the idea about async calls. You will never know how long an async call will take, so you can never set the appropriate timeout.
I believe you are misunderstanding the usage of queue from async library you described. The documentation for the queue can be found here.
Copy pasting the documentation in here, in-case things are changed or down:
Creates a queue object with the specified concurrency. Tasks added to the queue are processed in parallel (up to the concurrency limit). If all workers are in progress, the task is queued until one becomes available. Once a worker completes a task, that task's callback is called.
The above means that the queue can simply be used to priorities the async task a given worker can perform. The different async tasks can still be finished at different times.
Potential solutions
There are a few solutions to your problem, depending on your requirements.
You can only send one async call at a time and wait for the first one to finish before sending the next one
You store the results and only display the results to the user when all calls have finished
You disregard all calls except for the latest async call
In your case I would pick solution 3 as your are searching for something. Why would you use care about the results for "a" if they are already searching for "abc" before they get the response for "a"?
This can be done by giving each request a timestamp and then sort based on the timestamp taking the latest.
SOLUTION:
Server:
exports = module.exports = function(io){
io.sockets.on('connection', function (socket) {
socket.on('result', function(data, fn) {
var filter = data.filter;
var counter = data.counter;
if(filter.length === 1 || filter.length === 5) { // TEST SYNCHRONOUSLY
setTimeout(function() {
fn({ filter: filter, counter: counter}); // return to client
}, 3000);
} else {
fn({ filter: filter, counter: counter}); // return to client
}
});
});
}
Client:
export class FilterableDataTable extends Component {
constructor(props) {
super();
this.state = {
endpoint: "http://localhost:3001",
filters: {},
counter: 0
};
this.onLazyLoad = this.onLazyLoad.bind(this);
}
onLazyLoad(event) {
var offset = event.first;
if(offset === null) {
offset = 0;
}
var filter = ''; // filter is the search character
if(event.filters.result2 != undefined) {
filter = event.filters.result2.value;
}
var returnedData = null;
this.state.counter++;
this.socket.emit('result', {
offset: offset,
limit: 20,
filter: filter,
counter: this.state.counter
}, function(data) {
returnedData = data;
console.log(returnedData);
if(returnedData.counter === this.state.counter) {
console.log('DATA: ' + JSON.stringify(returnedData));
}
}
This however does send unneeded data to the client, which in return ignores it. Somebody any idea's for further optimizing this kind of communication? For example a method to keep old data at the server and only send the latest?

Resources