solution for stopping the spinning numbers in angular 2 - javascript

happyCustomers = 0;
followers = 0;
awardsWinning = 0;
photosTaken = 0;
arrayOfImages = [];
constructor() { }
ngOnInit() {
Observable.interval(400).subscribe(x => {
this.happyCustomers = this.happyCustomers + 1;
});
Observable.interval(200).subscribe(x => {
this.followers = this.followers + 1;
});
Observable.interval(700).subscribe(x => {
this.awardsWinning = this.awardsWinning + 1;
});
Observable.interval(300).subscribe(x => {
this.photosTaken = this.photosTaken + 1;
});
}
here i am adding + 1 evrytime and it does not stop, when i reach a certain data count it should stop the count.

Use take method.
take method takes the first count values from the source, then completes.
Call it before subscription.
const certainNumber = 10;
Observable.interval(400)
.take(certainNumber)
.subscribe(_ => this.happyCustomers++);

If you have a hardcoded value, you can use the take() command.
Example :
Observable.interval(400)
.take(500)
.subscribe(x => {
this.happyCustomers = this.happyCustomers + 1;
});
The above code will stop after 500 events have been emitted.
See http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-take

Related

How to stop setTimeout loop if condition is met

I am making a screensaver that displays the elements inside an array with a delay between each iteration, to display the elements slowly one by one. My "onmousemove" event successfully removes the screensaver from the page, but the for loop in my startScreensaver() function keeps running when it should in fact break. What am I doing wrong?
see JSfiddle https://jsfiddle.net/m60k75jf/
const idletime = 2;
const screenSaver = document.getElementById("screensaver");
const stars = Array.from(document.querySelectorAll(".star"));
let mousetimeout;
let screensaverActive = false;
window.addEventListener("mousemove", (e) => {
clearTimeout(mousetimeout);
if (screensaverActive) {
stopScreensaver();
} else {
mousetimeout = setTimeout(function () {
startScreensaver();
}, 1000 * idletime);
}
});
function stopScreensaver() {
screensaverActive = false;
stars.forEach((star) => {
star.classList.remove("is--visible");
});
screenSaver.classList.remove("is--active");
}
function startScreensaver() {
screensaverActive = true;
screenSaver.classList.add("is--active");
for (let index = 0; index < stars.length; index++) {
if (screensaverActive) {
setTimeout(function () {
stars[index].classList.add("is--visible");
}, 2000 * index);
} else {
break;
}
}
}
You can't break the loop like that. Your loop creates all these setTimeout immediately. Your condition will just never trigger. What you'll need to do is clear out all those setTimeout. You can push them into an array.
let animationTimeouts;
function startScreensaver() {
animationTimeouts = [];
screensaverActive = true;
screenSaver.classList.add("is--active");
for (let index = 0; index < stars.length; index++) {
if (screensaverActive) {
animationTimeouts.push(setTimeout(function () {
stars[index].classList.add("is--visible");
}, 2000 * index));
} else {
break;
}
}
}
Then clear them out on stopScreensaver
function stopScreensaver() {
screensaverActive = false;
if(animationTimeouts) animationTimeouts.forEach(clearTimeout);
stars.forEach((star) => {
star.classList.remove("is--visible");
});
screenSaver.classList.remove("is--active");
}
You might also want to reconsider moving your CSS transition to .star.is--visible

Calling asynchronous socket.io io.emit() in synchronous way

Completely new to socket io here.
I want to emit data running from within a loop that I am running on my server. The loop runs for a while (a few seconds, up to a few minutes) and I want to emit data from my node server, that I then want to visualize on the client side in the browser (with chart js). Basically real time data charting.
The problem that occured is that apparently the emits are only fired after the loop has finished. From my understanding that is, because the emit function is asynchronous (non-blocking). And since the loop itself blocks until it is finished (originally used a while loop), the asynchronous calls are only fired afterwards. Correct?
while (CONDITION) {
...
io.emit('data update', DATA);
}
Now I have come up with my own solution, but it feels clonky. And I am wondering if there are better, more by the book ways to do this. What I am doing now is calling the loop step (where in the end I call emit) recursively as the callback to a setTimeout() with duration set to 0.
function loopStep() {
if (CONDITION) {
...
io.emit('data update', DATA);
setTimeout(loopStep,0)
}
}
setTimeout(loopStep,0);
My question is, are there other, better ways?
EDIT:
I was asked to add the full code.
Here is the full code code with the earlier (loop) version (as far as I can remember it, had to quickly reconstruct it here):
let stp = 0;
while (stp < maxLogStep) {
let new_av_reward;
var smallStep = 0;
while (smallStep < maxSmallStep) {
let ran1 = get_random();
let cum_prob = 0;
let chosen_action = pref.findIndex((el, i) => {
let pro = prob(i);
let result = pro + cum_prob > ran1;
cum_prob += pro;
return result;
})
let cur_reward = get_reward(chosen_action);
if (stp*maxSmallStep + smallStep == 0) {
new_av_reward = cur_reward
} else {
new_av_reward = prev_av_reward + (cur_reward - prev_av_reward) / (stp * maxSmallStep + smallStep)
}
let cur_prob = prob(chosen_action);
pref.forEach((element, index, array) => {
if (chosen_action === index) {
array[index] = element + stepSize * (cur_reward - new_av_reward) * (1 - cur_prob)
} else {
array[index] = element - stepSize * (cur_reward - new_av_reward) * (cur_prob)
}
});
prev_av_reward = new_av_reward;
smallStep++
}
io.emit('graph update', {
learnedProb: [prob(0), prob(1), prob(2)],
averageReward: new_av_reward,
step: stp * maxSmallStep + smallStep
});
stp++
};
And here is the recursive function with setTimeout:
function learnStep(stp, prev_av_reward) {
let new_av_reward;
if (stp < maxLogStep) {
var smallStep = 0;
while (smallStep < maxSmallStep) {
let ran1 = get_random();
let cum_prob = 0;
let chosen_action = pref.findIndex((el, i) => {
let pro = prob(i);
let result = pro + cum_prob > ran1;
cum_prob += pro;
return result;
})
let cur_reward = get_reward(chosen_action);
if (stp*maxSmallStep + smallStep == 0) {
new_av_reward = cur_reward
} else {
new_av_reward = prev_av_reward + (cur_reward - prev_av_reward) / (stp * maxSmallStep + smallStep)
}
let cur_prob = prob(chosen_action);
pref.forEach((element, index, array) => {
if (chosen_action === index) {
array[index] = element + stepSize * (cur_reward - new_av_reward) * (1 - cur_prob)
} else {
array[index] = element - stepSize * (cur_reward - new_av_reward) * (cur_prob)
}
});
prev_av_reward = new_av_reward;
smallStep++
}
io.emit('graph update', {
learnedProb: [prob(0), prob(1), prob(2)],
averageReward: new_av_reward,
step: stp * maxSmallStep + smallStep
});
setTimeout(learnStep, 0, stp + 1, new_av_reward);
};
If your goal is to make sure you hold up your while loop until the emit completes and the client receives and acknowledges that data, you may want to consider using Promises and socket.io acknowledgement feature like the below example:
Server Side
io.on("connection", async (socket) => {
// ...
while (CONDITION) {
// ...
await new Promise((resolve, reject) => {
io.emit('data update', data, (ack) => {
//client acknowledged received
resolve(true)
});
});
}
});
Client Side
socket.on("data update", (data, fn) => {
console.log(data)
fn("ack");
});

unsubscribe is not a function in an observable

I have a function that should be a base for a stopwatch. It returns some values for different methods. I use a subscribtion for an observable there, and I want to unsubscribe from it when my timer is stopped, but it returns an error "TypeError: this.customIntervalObservable.unsubscribe is not a function"
What might be the problem and how can I fix it?
My observable code:
customIntervalObservable = Observable.create((observer) => {
let count = 0;
setInterval(() => {
observer.next(count);
count = count + this.deg;
}, 1000);
});
My method code is:
stopWatch(isSubscribed) {
if (isSubscribed) {
this.customIntervalObservable.subscribe((sec) => {
this.ss = sec;
this.getSeconds(this.ss);
if (this.ss / (60 * this.deg) === 1) {
this.ss = 0;
this.mm = this.mm + 6;
this.getMinutes(this.mm);
if (this.mm / (60 * this.deg) === 1) {
this.mm = 0;
this.hh = this.hh + 6;
this.getHours(this.hh);
}
}
});
} else {
this.customIntervalObservable.unsubscribe();
}
}
You can't unsubscribe an Observable, just a Subscription.
First, get a reference to that subscription:
stopWatch(isSubscribed) {
if (isSubscribed) {
this.subscription = this.customIntervalObservable.subscribe(...)
...
}
}
Then, in the else-path, you can write:
else {
this.subscription.unsubscribe();
}

How would I change this code to a different format? (Javascript)

The code below is the code that I have written:
function singer(artist) {
var songs = [];
for(var i = 0; i < music.length;i++ ){
if(music[i].artist.indexOf(artist) > -1) {
songs.push(music[i].name);
}
}
return songs;
}
The code that I want to look similar to the function singer(artist) code is this:
const genreCount = () => {
const genres = music.reduce((result, cur) => {
cur.genres.forEach(g => {
if (result.hasOwnProperty(g)) {
result[g] += 1;
}
else
result[g] = 1;
});
return result;
}, {});
return genres;
}
I am unfamiliar with this type of format in Javascript, how would I change it so that const genreCount will look like function singer(artist).
This is what you will get if you want to change that function:
function genreCount() {
const genres = music.reduce(function(result, cur) {
cur.genres.forEach(function(g) {
if (result.hasOwnProperty(g)) {
result[g] += 1;
}
else
result[g] = 1;
});
return result;
}, {});
return genres;
}
or (if you want to assign that fucntion to a const anyway):
const genreCount = function() {
const genres = music.reduce(function(result, cur) {
cur.genres.forEach(function(g) {
if (result.hasOwnProperty(g)) {
result[g] += 1;
}
else
result[g] = 1;
});
return result;
}, {});
return genres;
}
You just should replace arrow functins with the regular function expressions. But I don't know why do you need that.
this style is called functional programming
const singer = artist => music.filter(m => artist.indexOf(m.artist) > -1).map(m => m.name)
here is a good interactive tutorial if you are interested
Functional Programming in Javascript
UPDATE:
oops, sorry for misunderstanding your questions
here is genreCount rewritten with for-loop:
function genreCount(){
const genres = {};
for(var i=0; i<music.length; i++){
var g = music[i]
if (genres.hasOwnProperty(g)) {
genres[g] += 1;
}
else{
genres[g] = 1;
}
}
return genres;
}

How can I iterate up and down a large javascript array in chunks?

I'm trying to write a function that takes a large array and iterates up and down it in a set number of chunks via a previous and next button. I have the next button working fine but cannot get it to reverse the array the same way I go forward. Here's what I have:
Javscript
success: function(data) {
var body = data;
console.log(body.length);
//body/data is a string
var text = body.split(' ');
text.chunk = 0; text.chunkSize = 15;
var next = true;
var increment = function(array,next) {
if (array.chunk < array.length) {
var slice = array.slice(
array.chunk,
Math.min(array.chunk + array.chunkSize, array.length));
var chunk = slice.join(" ");
if (next) {
array.chunk += array.chunkSize;
$( '#test' ).html('<p>' + chunk + '</p>');
}
else {
var slice = array.slice(
array.chunk,
Math.min(array.chunk+array.chunkSize, array.length));
array.chunk -= array.chunkSize;
$( '#test' ).html(chunk);
}
}
}
$("#prev").click(function() {
increment(text);
});
$("#button").click(function() {
increment(text, next);
});
}
success: function(data) {
var body = data;
console.log(body.length);
//body/data is a string
var text = body.split(' ');
text.chunk = 0; text.chunkSize = 15;
var increment = function(array,next) {
if(next) {
array.chunk = Math.min(array.chunk + array.chunkSize, array.length);
} else {
array.chunk = Math.max(array.chunk - array.chunkSize, 0);
}
var slice = array.slice(
array.chunk,
Math.min(array.chunk + array.chunkSize, array.length));
var chunk = slice.join(" ");
}
$("#prev").click(increment(text,false));
$("#button").click(increment(text, true));
}
Is this what you need? Fastly coded, and without testing so use with caution.
Okay, so first of all I really suggest breaking up your code. It looks like this is a response back from a server. I would in the response from the server, parse the data just like you are (side note, why don't you just return json from the server?) but use a call back to handle pagination.
var returnData = data.split(' ');
addPagination(returnData);
Under addPagination I would handle the DOM manipulation:
function addPagination(array) {
$('#container').show();
var incremental = incrementArray(array);
var responseSpan = $('#response');
$('#previous').click(function() {
incremental.previous();
showText();
});
$('#next').click(function() {
incremental.next();
showText();
});
showText();
function showText() {
responseSpan.text(incremental.array.join(', '));
}
}
But to handle the actual shifting of the array, I would use some function outside of your own. This uses Object Oriented JavaScript, so it is a bit more complex. It stores the original array in memory, and has 2 methods (next, previous), and has 1 attribute (array):
function incrementArray(array) {
_increment = 2;
var increment = _increment < array.length ? _increment : array.length;
var index = -2;
this.next = function () {
var isTopOfArray = index + increment > array.length - increment;
index = isTopOfArray ? array.length - increment : index + increment;
this.array = array.slice(index, index + increment);
return this.array;
};
this.previous = function () {
var isBottomOfArray = index - increment < 0;
index = isBottomOfArray ? 0 : index - increment;
this.array = array.slice(index, index + increment);
return this.array;
};
this.next();
return this;
}
To test it, use this jsFiddle.

Categories