I am working with React and socket.io, every time an event happens there is a socket listening to any changes.
I have this in my actions
socket.on('playerCount', (data) => {
PlayerSlotActions.updatePlayerAmount({
playerAmount : data.cardCount,
position : data.position,
});
})
where the data param returns this
{
playerAmount : 10,
position : 2
}
sometimes from the socket, comes from 4 to 7 events in one shot in order to change the playerAmount key to another value. Every time I receive that event, I should change the number of the playerAmount to the new number sent from the socket.
My issue:
lets say in the view I have something like Player Amount: 10, and then the socket sends the numbers 1, 3 and 5 in one shot, so the new amount would change to 19 which is the sum of the new number, and that is OK, but that change shouldn't happen quick, between the sum of one number and another, there should be 5 seconds of difference, like:
Player Amount: 10
after 5 seconds...
Player Amount: 11
after 5 seconds...
Player Amount: 14
after 5 seconds...
Player Amount: 19
... and so on.
so I am trying to figure out which is the best method to use here. With setTimeout, it does what I want but only the first attempt, the rest of the sums takes around 1 second of difference even if you put a timeout for 5 seconds.
I am using lodash, so I thought maybe _.debounce, _.throttle or _.delay methods could help, but I was wrong. Only delay works the same way as setTimeout
I did it like this
socket.on('playerCount', (data) => {
setTimeout(function() {
PlayerSlotActions.updatePlayerAmount({
playerAmount : data.cardCount,
position : data.position,
});
}, 5000);
});
I am just learning this. Is there a way like to store the new numbers in an array or something like this ?
Tell me your recommendations.
Just in case you want to see my code
the code above is in my actions, from the actions, it goes to the stores
#bind(PlayerSlotActions.updatePlayerAmount)
updatePlayerAmount (data) {
this.state.playerSlots[data.position - 1].playerAmount = data.playerAmount;
}
and from there, it goes directly to the component
componentDidUpdate () {
let playerAmount = this.props.playerAmount;
this.refs.playerAmount.getDOMNode().innerHTML = playerAmount;
}
UPDATE
connect () {
socket = io.connect('localhost:1101/server');
socket.on('playerCount', (data) => {
console.log('1');
queue.push({
playerAmount : data.cardCount,
position : data.position,
});
})
setTimeout(function() {
if (queue.length > 0) {
var data = queue.splice(0, 1)
PlayerSlotActions.updatePlayerAmount(data);
}
}, 5000);
}
So you had problems putting in my code from this question: How to do a queue in order to wait 5 seconds to execute a function
I don't know if you solved it with Will Newton now, but in case you didn't, try this:
// variables for the queue and a boolean to
// indicate whether the queue gets worked on or not
var queue = [];
var notRunning = true;
connect () {
socket = io.connect('localhost:1101/server');
socket.on('playerCount', (data) => {
console.log('1');
queue.push({
playerAmount : data.cardCount,
position : data.position,
});
startQueue();
})
// Initiating function, has to be seperate because else each socket connect
// would trigger a new instance of processQueue, which would result
// in shorter times than 5s between the updates
function startQueue(){
if(notRunning){
notRunning = false;
processQueue();
}
}
// Recursive function, calls itself every 5 seconds as long as there are
// items in the queue
function processQueue(){
if(queue.length > 0){
setTimeOut(function(){
// passes the first item of the queue in your function
PlayerSlotActions.updatePlayerAmount(queue[0]);
// removes first item from the queue
queue.shift();
processQueue();
}, 5000)
}else{
notRunning = true;
}
}
}
Yes, your idea about storing data in an array will work.
We can treat the array like a queue.
However, you are limited to it only updating every 5 seconds.
var queue = [];
socket.on('playerCount', (data) => {
queue.push({
playerAmount : data.cardCount,
position : data.position,
});
});
setInterval(function() {
if (queue.length > 0) {
var data = queue.shift();
PlayerSlotActions.updatePlayerAmount(data);
}
}, 5000);
Related
I have some websocket that sends around 100's of data per second,I want to limit it to only 1 data per 500 ms.
onMessage(data) {
console.log(data); // This prints around 100 different times within 1 second
}
I tried something like below , Is this the right approach or is there any other better way to do it ? because this code runs 100 times per second.
var lastlog = new Date().getTime();
onMessage(data) {
currenttime = new Date().getTime();
if ( currenttime - lastlog > 500) {
console.log(data);
lastlog = new Date().getTime();
}
}
P.s : I can ignore remaining data and will be able to reduce the 500 ms to 200ms.. that is 5 data per second.
Here is another way of doing it, using the npm package throttle-debounce. This method is not "better". It can result is less code typed but you might not want the dependency on the package.
You can use the throttle function and specify how many milliseconds until it can be called again. Setting the second argument to true prevents the last request from being deffered -https://www.npmjs.com/package/throttle-debounce#notrailing.
The example below uses the library to throttle how often a button is pressed.
const { throttle } = throttleDebounce
const handleRequest = throttle(500, true, () => {
console.log('this request will be handled')
})
<script src='https://cdn.jsdelivr.net/npm/throttle-debounce#3.0.1/umd/index.js'></script>
<button onClick="handleRequest()">Mimick sending message</button>
Your use case might look like this:
import { throttle } from 'throttle-debounce'
const onMessage = throttle(500, true, () => {
console.log(data);
})
Less lines than your example, but that doesn't mean it's "better".
I have created an API that takes user input and process it. However, the process takes more than 5 seconds (dialogflow limit).
How can I continue with other processes until this certain process is finished?
Or is it possible to return to the user any messages like "Please hold on a bit..." so it can restart the time.
var message = "hi"; //test purpose
async function GetCertain_info(agent) {
await uiFx();
agent.add("Here are the information on " + message);
}
async function uiFx() {
var {
ui
} = require('./uia.js');
return new Promise(function(resolve, reject) {
ui().then((msg) => {
console.log("Destination Message : " + msg)
message = msg;
resolve(message);
}).catch((msg) => {
console.log(msg)
message = msg;
reject(message);
})
});
}
Appreciate your help
Yes, it is possible to return to the user any messages like "Please hold on a bit…" by setting up a FollowupEvent.
You can extend the 5 seconds Intent limit up to 15 seconds by setting up multiple follow-up events. Currently, you can only set up 3 follow-up events one after another (which can extend the timeout up to 15 seconds).
Here's an example of how you can do it in the fulfillment:
function function1(agent){
//This function handles your intent fulfillment
//you can initialize your db query here.
//When data is found, store it in a separate table for quick search
//get current date
var currentTime = new Date().getTime();
while (currentTime + 4500 >= new Date().getTime()) {
/*waits for 4.5 seconds
You can check every second if data is available in the database
if not, call the next follow up event and do the
same while loop in the next follow-up event
(up to 3 follow up events)
*/
/*
if(date.found){
agent.add('your data here');//Returns response to user
}
*/
}
//add a follow-up event
agent.setFollowupEvent('customEvent1');
//add a default response (in case there's a problem with the follow-up event)
agent.add("This is function1");
}
let intentMap = new Map();
intentMap.set('Your intent name here', function1);;
agent.handleRequest(intentMap);
In my app, I need to run my app offline because drivers have to use that app and in remote areas, they might not find internet for that purpose I need to download all data in advance through Axios request at the time they have internet. so that request can get data from the cache instead of server.
At the moment i tried this but this doesn't seems to be good practice
tryOffileWorkSheet: async function () {
Network.addListener("networkStatusChange", (status) => {
if (status.connected) {
setInterval(function () {
let worksheet = JSON.parse(localStorage.getItem("worksheet"));
if (worksheet == null) {
worksheet = [];
}
// localStorage.removeItem('worksheet')
for (let i = 0; i <= worksheet.length; i++) {
if(worksheet.length > 0){
setTimeout(function () {
if(worksheet[i]?.work_order_id){
ApiService.get(
`/api/gangBoss/work-sheet/${worksheet[i].work_order_id}/${worksheet[i].column_name}/${worksheet[i].value}`
).then((response) => {
if(response.data.status){
worksheet.splice(i,1)
localStorage.setItem("worksheet", JSON.stringify(worksheet));
}
console.log('After', worksheet)
// this.closeModal();
});
}
},i* 3000);
}
}
}, 3000);
}
});
},
also for this, it is necessary for the user to behave on this page when the internet available it means that we need to download a lot of data.
Can you please tell me the best practice to do that or plugin for vue which can help for that
The question is sort of unclear and is not maybe the best fit for SO, but I'll try to answer.
The first thing I noticed here is the use of setInterval, not that you are using it, but rather how. There's nothing that stops the interval from running. Here's a scenario that I hope illustrates the problem
networkStatusChange fired: status.connected === true
setInterval creates interval #1
3 seconds later interval #1 fires
... this keeps happening for the next 2 hours
networkStatusChange fired: status.connected === false
... interval#1 keeps firing
networkStatusChange fired: status.connected === true
setInterval creates interval #2
within 3 seconds interval #1 fires
3 seconds later interval #2 fires
... intervals #1 and #2 keep firing (twice within 3 seconds)
networkStatusChange fired: status.connected === false
networkStatusChange fired: status.connected === true
setInterval creates interval #3
within 3 seconds interval #1 fires
within 3 seconds interval #2 fires
3 seconds later interval #3 fires
... intervals #1, #2 and #3 keep firing (three within 3 seconds)
So there's two problems. The interval keeps firing regardless of whether the system is still connected.
You might be better of just firing every 3 seconds with a single timer, and exiting if the connection is not available. This also allows using window.navigator.onLine, which has much better browser support.
I don't know how long you expect worksheet to be, but if you have the ApiService called through setTimeout 3 seconds apart, and then call the parent function every 3 seconds, you're going to be sending a lot of requests.
tryOffileWorkSheet: function () {
// (need to create `intervalId` in data)
if (this.intervalId) clearInterval(this.intervalId);
this.intervalId = setInterval(() => {
if (window.navigator.onLine) {
this.getWorkSheet();
}
}, 3000);
},
getWorkSheet: function () {
let worksheet = JSON.parse(localStorage.getItem("worksheet"));
if (worksheet == null) {
worksheet = [];
}
for (let i = 0; i <= worksheet.length; i++) {
if (worksheet.length > 0) {
setTimeout(() => {
if (worksheet[i]?.work_order_id) {
ApiService.get(
`/api/gangBoss/work-sheet/${worksheet[i].work_order_id}/${worksheet[i].column_name}/${worksheet[i].value}`
).then((response) => {
if (response.data.status) {
worksheet.splice(i, 1);
localStorage.setItem("worksheet", JSON.stringify(worksheet));
}
console.log("After", worksheet);
});
}
}, i * 300);
}
}
},
I have this code here:
server:
io.emit('attack-damage', {
damage: damage,
attacker: username,
});
client:
socket.on('attack-damage', (data) => {
setTimeout(() => this.damageVisible = false, 1000);
});
What it does is when user clicks attack it emits and shows damage of the user that fades away after one second. Problem is that let's say if I click attack my damage is being shown but also if another player clicks attack then it's damage is being shown and mine damage fades out faster than after 1 second it just replaces the new damage from another player. How to show multiple damages on the screen and not just one ?
EDIT
socket.on('attack-damage', (data) => {
this.damage = data.damage;
this.aryDamage.push(data.damage, data.attacker);
if (!this.bolDamageRunning) {
if(this.aryDamage.length != 0) {
this.bolDamageRunning = true;
setTimeout(() => {
this.damageVisible = false;
this.aryDamage.splice(0,1);
this.bolDamageRunning = false;
}, 2000);
} else {
}
} else {
}
setTimeout(() => this.damageVisible = true, 2000);
When I use above code it makes appear damage after two seconds after the click. But also if I use two users, then old damage from screen is being replaced with new one
create a global array called aryDamage.
create a global boolean called bolDamageRunning.
client socket.on('attack-damage'), add a new element to the array, e.g.
aryDamage.push(data);
create a timer using setInterval to read the aryDamage every 100 ms.
function thisTimerCalledEvery100MS() {
// run Damage only when last Damage finish
if (!bolDamageRunning) {
// Check whether another damage waiting
if (aryDamage.length != 0) {
// Set running true
bolDamageRunning = true;
// call funDamage after 1 second
setTimeout(funDamage, 1000);
} else {
// No command waiting, do nothing
}
} else {
// Another command running, do nothing
}
}
function funDamage() {
// Your code to show damange, or
// do something with the first element of aryDamage
this.damageVisible = false;
// remove top element from aryDamage
aryDamage.splice(0,1);
// Set running = false
bolDamageRunning = false;
}
This technique makes sure the last 'Command' finishes before executing next 'Command'.
I'm trying to use Twitter API in order to retweet.And because Twitter has limitation to 2400 actions per day I decided to limit the retweet to one time every 40 seconds.
I'm using https://www.npmjs.com/package/twit using Streaming API.
My problem is that it continuously streams console.log instead of using setInterval.
By stream I mean it outputs console.log multiple times instead of one time.
function hastagRetweet() {
var stream = T.stream('statuses/filter', { track: ['#hastag']})
stream.on('tweet', function (tweet,error) {
var retweetId = tweet.id; // id
var retweetId_str = tweet.id_str;
var tweetTextRetweet = tweet.text; // tweet text
function twInterval() {
console.log('INFO ------- ',tweet.id);
console.log('INFO ------- ',tweet.text);
};
setInterval(twInterval, 1000 * 40);
});
}
Any way of getting the variales data retweetId,retweetId_str,tweetTextRetweet outside stream.on beside inserting them into a mysql table so that every 40 seconds it checks the variables fixed data and executes console.log?
There are multiple problems here:
You're trying to start an interval timer in an event handler. That means that every time the event fires you would be attempting to start a new interval timer and they would pile up.
Even if you did sucessfully start up an interval timer, each one would never change it's output since the variables in scope for it never change as it is started up within a given function and the arguments to that function are what they were when it was first called. Subsequent calls of the function will start a new function not change the arguments on the prior call of the function.
You aren't even starting your interval correctly. As it is, all you're doing is calling the function and passing it's return value to setInterval() which does nothing.
If the goal is just to output to the console each stream tweet event, then probably what you want is just this:
function hastagRetweet() {
var stream = T.stream('statuses/filter', { track: ['#hastag']})
stream.on('tweet', function (tweet,error) {
var retweetId = tweet.id; // id
var retweetId_str = tweet.id_str;
var tweetTextRetweet = tweet.text; // tweet text
console.log('INFO ------- ',tweet.id);
console.log('INFO ------- ',tweet.text);
});
}
You cannot get these variables outside the .on() handler. It's an async callback and the only place they reliably exist is within that handler.
If you can describe in more detail what the end result you're trying to achieve is, we can likely help more specifically. If you want to do something every 40 seconds, then maybe you need to collect data in some collection (probably an array) and then every 40 seconds evaluate what you have just recently collected.
Here's a method of collecting the events in an array and then evaluating them every 40 seconds:
function hastagRetweet() {
var stream = T.stream('statuses/filter', { track: ['#hastag']});
var tweets = [];
stream.on('tweet', function (tweet,error) {
tweets.push({id: tweet.id, str: tweet.id_str, text: tweet.text});
});
setInterval(function() {
// evaluate the tweets in the tweets array every 40 seconds
// do something with them
for (var i = 0; i < tweets.length; i++) {
// tweets[i].id
// tweets[i].str
// tweets[i].text
}
// reset the tweets array
tweets.length = 0;
}, 40 * 1000);
}
Please note that once you call hastagRetweet(), it will run forever.