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();
}
Related
i just want to call this code as a function inside an IntersectionObserver:
And i want it to be reusable and cleaner, so i could just it anywhere
//I want this to be a function
numbers.forEach((number, index) => {
intervals[index] = setInterval(() => {
if(counters[index] === parseInt(number.dataset.num)){
clearInterval(counters[index]);
} else{
counters[index] += 1;
number.textContent = counters[index] + "%";
svgEl[index].style.strokeDashoffset = Math.floor(472 - 440 * parseFloat(number.dataset.num / 100));
}
}, 20);
});
}
My IntersectionObserver
const animate = new IntersectionObserver(function (entries, animate) {
entries.forEach(entry => {
if(!entry.isIntersecting) {
//function
}
});
});
animate.observe(test);
I don't see the point!
Enclose your function in function name either function... or const...
and call it in intersection observer
function numbersCount(numbers) {
numbers.forEach((number, index) => {
intervals[index] = setInterval(() => {
if(counters[index] === parseInt(number.dataset.num)){
clearInterval(counters[index]);
} else{
counters[index] += 1;
number.textContent = counters[index] + "%";
svgEl[index].style.strokeDashoffset = Math.floor(472 - 440 * parseFloat(number.dataset.num / 100));
}
}, 20);
});
}
}
Now that means that "numbers" is known somewhere!
From what you are showing, I can't guess
Currently I am trying to learn how to use es6 classes for an application I am working on.
The goal is to increase the age of an animal based on the current date.
The Animal Class
Animal.js
class Animal {
constructor(name, age, species, gender, activity, birthday, hunger = 100, stamina = 100, happy = 100) {
this.name = name;
this.age = age;
this.species = species;
this.gender = gender;
this.activity = activity;
this.hunger = hunger;
this.stamina = stamina;
this.happy = happy;
this.birthday = birthday
}
birthday() {
this.age++
console.log(`Happy birthday to ${this.name}, ${this.name} is now ${this.age}`)
}
}
module.exports = Animal
The Time Class
Time.js
class Time {
constructor() {
this.seconds = 0;
this.minutes = 0;
this.days = 1;
this.season = 1;
this.year = 0;
this.clock = `${this.minutes}:${this.seconds}`;
this.monthDate = `${this.season}/${this.days}`
this.yearDate = `${this.season}/${this.days}/${this.year}`
this.timeStamp = `${this.season}/${this.days}/${this.year}-${this.minutes}:${this.seconds}`
// Start timer when
this.countTime = setInterval(this.increaseTime.bind(this), 1000)
}
increaseTime() {
this.clock = `${this.minutes}:${this.seconds}`
this.monthDate = `${this.season}/${this.days}`
this.yearDate = `${this.season}/${this.days}/${this.year}`
this.timeStamp = `${this.season}/${this.days}/${this.year}-${this.minutes}:${this.seconds}`
console.log(this.clock)
if (this.seconds === 60) {
this.seconds = 0;
this.minutes++
if (this.minutes === 1) {
this.minutes = 0;
this.days++
console.log(this.timeStamp)
if (this.days === 15) {
this.days = 0;
this.season++
if (this.season > 4) {
this.season = 1;
this.year++
}
}
}
} else {
this.seconds++
}
}
}
module.exports = Time;
Here is where I try to utilize them both:
Zoo.js
const Animal = require("./Animal");
const Time = require("./Time");
let time1 = new Time();
let gary = new Animal("Gary", 10, "Gorilla", "Male", "Sleeping", '1/2')
if (time1.monthDate === gary.birthday) {
gary.birthday()
}
So based off of the value of time1.monthDate (which is always changing because I have a setInterval function within the Time class), I will execute a function within the Animal Class to increase Gary's age.
I realize that in this current state, that I need to constantly run this check, so I have also tried this:
while (time1) {
if (time1.monthDate === gary.birthday) {
gary.birthday();
}
}
This also does not work although I am constantly running this check.
Any help would be appreciated, thanks in advance!
This also does not work although I am constantly running this check.
That's because no other code can run while that loop runs. That includes the setInterval callback that increases the time.
Instead, why don't you provide a way to schedule a callback to the timer tick:
class Time {
constructor() {
this.seconds = 0;
this.minutes = 0;
this.days = 1;
this.season = 1;
this.year = 0;
this.handlers = []; // < collect handlers
// Start timer when
this.countTime = setInterval(this.increaseTime.bind(this), 1000)
}
get clock() { // if this is a getter, it is always up to date
return `${this.minutes}:${this.seconds}`;
}
get monthDate() {
return `${this.season}/${this.days}`;
}
get yearDate() {
return `${this.season}/${this.days}/${this.year}`;
}
get timeStamp () {
return `${this.season}/${this.days}/${this.year}-${this.minutes}:${this.seconds}`;
}
increaseTime() {
this.seconds++
if (this.seconds === 60) {
this.seconds = 0;
this.minutes++;
}
if (this.minutes === 1) {
this.minutes = 0;
this.days++;
}
if (this.days === 15) {
this.days = 0;
this.season++
}
if (this.season > 4) {
this.season = 1;
this.year++
}
this.handlers.forEach(h => h(this)); // < trigger handlers
}
onTick(h) { this.handlers.push(h); }
}
Then you can do:
time1.onTick(() => {
if (time1.monthDate === gary.birthday) {
gary.birthday();
}
});
I want to automatic go through random items from the DropDownList1.
It's working, but it's going by the order first to last, and I want to go through items randomly.
/* function to automatic select DropDownList1 items */
function selectFromDropdown(selector, text) {
$(selector).find('option').each(function() {
if ($(this).text() == text) {
$(selector).val($(this).val());
return false;
}
})
}
$(document).ready(function() {
let numberOfTimes = 0;
const time = 1000 //3s
let values = [];
$('#DropDownList1').find('option').each(function() {
values.push($(this).text())
});
console.log(values);
const interval = setInterval(function() {
selectFromDropdown('#DropDownList1', values[numberOfTimes])
if (numberOfTimes == values.length - 1) {
clearInterval(interval);
} else {
numberOfTimes = numberOfTimes + 1;
}
},
time);
});
Here the snnipet: https://jsfiddle.net/lucasangelo_/17Lgr0kc/6/
If you want to get random values from a select, then you can use the next function:
function getRandomValuesFromSelect(selector, numberOfItemsWanted)
{
var valuesSelected = [];
var childrenSelect = document.getElementById(selector).children;
for (var i = 0; i < numberOfItemsWanted; i++) {
var randomValue = Math.floor(Math.random() * childrenSelect.length);
var randomOption = childrenSelect[randomValue];
if (valuesSelected.indexOf(randomOption.value) < 0) {
valuesSelected.push(randomOption.value);
} else {
i--;
}
}
return valuesSelected;
}
Then you could call it like so:
getRandomValuesFromSelect("DropDownList1", 3);
The answer is:
/* function to automatic select DropDownList1 items */
function selectFromDropdown(selector, text) {
$(selector).find('option').each(function() {
if ($(this).text() == text) {
$(selector).val($(this).val());
return false;
}
})
}
function getRandomNumber(min, max) {
return (Math.random() * (max - min) + min).toFixed(0);
}
$(document).ready(function() {
let numeroDeVezes = 0;
const tempoEntreCadaChamada = 1000 //3s
let valores = [];
$('#DropDownList1').find('option').each(function() {
valores.push($(this).text())
});
console.log(valores);
const interval = setInterval(function() {
const randomNumber = getRandomNumber(0, valores.length - 1);
const randomItem = valores[randomNumber];
//console.log(randomItem);
selectFromDropdown('#DropDownList1', randomItem),
console.log(`${numeroDeVezes} - Chamou do PostBack para ${randomItem}`);
//__doPostBack('LButton3', 'OnClick');
if (numeroDeVezes == valores.length - 1) {
console.log("Percorreu todos, mata o setInterval");
clearInterval(interval);
} else {
numeroDeVezes = numeroDeVezes + 1;
}
},
tempoEntreCadaChamada);
});
Thank you boys!
I am trying to implement two classes that can deal with asynchronous tasks in JavaScript:
Class Task: mimics the execution of a task with setTimeout. Once the timer expires, the task is considered completed.
Class TaskManager: has a capacity parameter to limit the numbers of tasks that can be executing in parallel.
I thought if I could just call the loop function recursively, just to keep checking if one job is done, I could proceed to the next job. But this leads immediately to a "Maximum call stack size exceeded" error.
Can someone explain how I can fix this?
class Task {
constructor(time) {
this.time = time;
this.running = 0;
}
run(limit, jobs, index) {
setTimeout(() => {
console.log('hello', index);
this.done(limit, jobs, index);
}, this.time);
}
done(limit, jobs, index) {
jobs.splice(index, 1);
console.log(jobs);
}
}
class TaskManager {
constructor(capacity) {
this.capacity = capacity;
this.jobs = [];
this.index = 0;
this.running = 0;
this.pending = [];
}
push(tk) {
this.jobs.push(tk);
this.index += 1;
const loop = () => {
if (this.jobs.length === 0) {
return;
}
if (this.jobs.length <= this.capacity) {
this.running += 1;
tk.run(this.capacity, this.jobs, this.index-1);
return;
}
loop();
}
loop();
}
}
const task = new Task(100);
const task1 = new Task(200);
const task2 = new Task(400);
const task3 = new Task(5000);
const task4 = new Task(6000);
const manager = new TaskManager(3);
manager.push(task);
manager.push(task1);
manager.push(task2);
manager.push(task3);
manager.push(task4);
You should not implement the busy loop, as that will block the event loop and so no user UI events or setTimeout events will be processed.
Instead respond to asynchronous events.
If you let the setTimeout callback resolve a Promise, it is not so hard to do.
I modified your script quite drastically. Here is the result:
class Task {
constructor(id, time) {
this.id = id;
this.time = time;
}
run() {
console.log(this + ' launched.');
return new Promise(resolve => {
setTimeout(() => {
console.log(this + ' completed.');
resolve();
}, this.time);
});
}
toString() {
return `Task ${this.id}[${this.time}ms]`;
}
}
class TaskManager {
constructor(capacity) {
this.capacity = capacity;
this.waiting = [];
this.running = [];
}
push(tk) {
this.waiting.push(tk);
if (this.running.length < this.capacity) {
this.next();
} else {
console.log(tk + ' put on hold.');
}
}
next() {
const task = this.waiting.shift();
if (!task) {
if (!this.running.length) {
console.log("All done.");
}
return; // No new tasks
}
this.running.push(task);
const runningTask = task.run();
console.log("Currently running: " + this.running);
runningTask.then(() => {
this.running = this.running.filter(t => t !== task);
console.log("Currently running: " + this.running);
this.next();
});
}
}
const a = new Task('A', 100);
const b = new Task('B', 200);
const c = new Task('C', 400);
const d = new Task('D', 5000);
const e = new Task('E', 6000);
const manager = new TaskManager(3);
manager.push(a);
manager.push(b);
manager.push(c);
manager.push(d);
manager.push(e);
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