Angular $timeout calls function only once - angularjs

I am using AngulaJs and Ionic.
I am trying to call a function every 3 seconds for 10 times only (this is why I am not using $interval). Thing is, the code below calls the function only once (while the debugging code to the console is called 10 times).
for (i = 0; i < 10; i++)
{
$timeout(function () {
$scope.play(); // this is the called function
console.log("progress bar: " + i);
}, 3000);
}
Any help will be appreciated,
Thanks in advance,
Pagugim

For loop will start 10 timeouts all together. But seems like you want them to execute one after another. For this case you can use recursion.
var count = 0;
replay();
function replay() {
$timeout(function () {
if(count < 10) {
$scope.play();
count++;
replay(); // call this function again
console.log("progress bar: " + $scope.progressVal);
}
}, 3000);
}

I am trying to call a function every 3 seconds for 10 times only
You can use the optional count argument of $interval.
var i=0;
function reportProgress() {
$scope.play(); // this is the called function
console.log("progress bar: " + i);
i++;
};
$interval(reportProgress, 3000, 10);
From the Docs:
$interval
Usage
$interval(fn, delay, [count], [invokeApply], [Pass]);
count (optional) Number of times to repeat. If not set, or 0, will repeat
indefinitely. (default: 0)
-- AngularJS $interval API Reference

Actually, the $scope.play() is called 10 times, but at almost the same time. To call the function every 3 seconds, you can use a closure to keep the value i and set timeout as 3000*i. I think this is what you want:
for (i = 0; i < 10; i++) {
(function(i) {
$timeout(function() {
$scope.play();
console.log("progress bar: " + i)
}, 3000 * i);
})(i);
}
Jsfiddle: https://jsfiddle.net/ealonwang/ens9wu0g/15/. Watch the value and console log change every 3 seconds.

Related

How to update certain element of array in TypeScript

I have an array looking like this
[0...99]
0: {field1: 'test1', field2: test2}
1: {field1: 'test3', field2: test4}
...
I created the array like this
array:any = [];
And I added data using push() how can i update the data of element n later?
Initialisation part
for(this.j = 0; this.j< this.accNum; this.j++) {
this.array.push({ field1:'test1', field2:'' });
this.http1
.get(this.url.concat(this.array[this.j]?.field1))
.subscribe((response1: any) => {
this.array[this.j].field2 = 'test2';
});
}
Ok, seeing your code in the comment, I think I now know what it's happening.
The problem is that you are using a variable whose value has long been updated. Take a look at the following code:
let i;
for (i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
Notice the following:
The i variable is declared outside of the loop (this mimics your this.j behaviour).
The loop finishes completely before the first console.log has a chance to run (basically, the setTimeout make the code to run asynchronously). In this case, the setTimeout mimics your API call.
By the time the setTimeout runs, the loop has completely run leaving the i variable with the value of 5. So, sure enough, if you run this code you will see that you get 5 printed 5 times instead of the expected 0, 1, 2, 3, and 4.
Also notice that the printed value is 5 instead of the last "correct" value for this loop which would be 4. If you translate this to arrays, the result is that you are always accessing the element length + 1 which, obviously it does not exist.
What you need to do to fix your problem is to scope the the value of this.j inside the loop. For example:
let i;
for (i = 0; i < 5; i++) {
((k) => {
setTimeout(() => {
console.log(k);
}, 1000);
})(i);
}
With this code, what I'm doing it to create an anonymous function inside the loop and passing i to it (which gets it's value as k). By doing this, I'm setting k in the scope with the right value for the setTimeout and console.log to run.
Now, if you run this code, you will see that the result is, indeed, the expected one.
A more elegant way of doing this is by calling an external function instead:
function requestAPI(i) {
setTimeout(() => console.log(i), 1000);
}
let i;
for (i = 0; i < 5; i++) {
requestAPI(i);
}
Here it is a more suitable example for your case:
const values = [];
function requestAPI(i) {
setTimeout(() => {
values[i].field2 = 'test2';
}, 1000);
}
let i;
for (i = 0; i < 5; i++) {
values.push({ field1: 'test1', field2:'' });
requestAPI(i);
}
setTimeout(() => console.log(values), 2000);
You can run it here.
I suggest you search for the topic of variable scope in Javascript.

While loop in nightwatch.js

I'm trying to run the following code (a number of steps) several times (e.g: 10 times) so that:
the user navigates to the detailed url
a random button is clicked, as per the defined var values
this is repeated 10 times
the test is then complete
I'm working with the following NightwatchJS code:
var randomEmail = faker.internet.email()
var competitionReference = ['drawing_21715','drawing_21704']
var randomCompetitionReference = competitionReference[Math.floor(Math.random()*competitionReference.length)]
module.exports = {
'navigate to homepage': function (browser) {
browser
.url('http://clickswin-stage.bellamagazine.co.uk/')
},
'select a competition': function (browser) {
browser
.useXpath()
.click('//*[#id="' + randomCompetitionReference + '"]/div/div[1]')
},
};
I've read that the best way to do this would be to use a while loop, but I'm not really sure how to set this up for my code above.
For example, if I were to use:
var i = 0
while ( i < 10) {
etc, whereabouts would I need to put this loop code within my code above?
Any help would be greatly appreciated.
One solution could be using a recursive function. Here is an example of how this could look like:
var randomEmail = faker.internet.email()
var competitionReference = ['drawing_21715', 'drawing_21704']
// var randomCompetitionReference = competitionReference[Math.floor(Math.random() * competitionReference.length)]
var randomCompetitionReference = function() {return competitionReference[Math.floor(Math.random() * competitionReference.length)]}
module.exports = {
'navigate to homepage': function (browser) {
browser
.url('http://clickswin-stage.bellamagazine.co.uk/')
},
'select a competition': function (browser, recursions) {
// Put the main code into a separat recursive function.
const doClick = function(times) {
if (times > 0) { // This is the equivalent to "while ( i < 10) {"
return browser
.useXpath()
.click('//*[#id="' + randomCompetitionReference() + '"]/div/div[1]')
.useCss()
.perform(()=>{ // perform() makes sure, that one function call is only executed after the other has fineshed (not concorrent)
return doClick(times -1)
})
} else {
return browser
}
}
doClick(recursions)
}
}
In your case you would call the function 'select a competition' with 10 as the parameter "recursions".
Note that I have changed "randomCompetitionReference" to be a function, so this generates a different value every time it is called. Else it would get one random value when its defined, and would reuse that same value for every click().

Angular Timeout calling function without delay

In the following snippet, the progress call is getting triggered continuously with out the 10s delay. Please let me know where i am going wrong
ctrl.progress =function (fileName){
if(ctrl.status < 100){
ctrl.timer = $timeout(function(fileName){
LookUpValueService.getImportStatus(fileName).
then(function(value){
ctrl.status = value;
ctrl.progress(fileName);
});
//ctrl.status = ctrl.status + 1;
}(fileName),5000);
}
else{
$timeout.cancel(ctrl.timer);
}
}; ctrl.progress("test.txt");
The first argument to $timeout() has to be a function itself, not the result of a call to a function.
In your case, ctrl.progress() does not return a function, it returns undefined, so when you have:
ctrl.timer = $timeout(ctrl.progress(fileName), 10000);
what happens is that ctrl.progress(fileName) gets called immediately, returns undefined, and then you tell $timeout() to wait 10 seconds before doing undefined.
Change that line to use a function instead, and it will work:
ctrl.timer = $timeout(() => ctrl.progress(fileName), 10000);
(or if not using ES6/2015/TypeScript: ctrl.timer = $timeout(function () { ctrl.progress(fileName) }, 10000);)

Arrays / loops with midi.js

I am looking to use a playsound function in midi.js to loop an array, with chords that i select, maybe 4 diff ones. But i can't figure it out. I can get it to do a single array, but not multiple, and it does not loop, only plays the amount of time I set it to (now 8).
window.onload = function () {
MIDI.loadPlugin({
soundfontUrl: "../MIDI.js/examples/soundfont/",
instrument: "acoustic_grand_piano",
onprogress: function(state, progress) {
console.log(state, progress);
},
onsuccess: function () {
for (var i = 0; i < 9; i++){
playsound([37,59,61,71,80])}
}});
var delay =1;
function playsound($chords)
{
var velocity = 127;
MIDI.setVolume(0, 127);
MIDI.chordOn(0, $chords, velocity, delay);
MIDI.chordOff(0, $chords, delay+1);
delay += 1;
}
Your code should work, except that for the timing to work predictably, I found you have to wait a bit after the success callback is called. If you call right after load, notes are played irregularly and out of sequence.
I recommend using a function like playChords below and testing well after load by calling the function with a button press. For example, this function plays three different chords at 1/2 second intervals, a total of 9 times.
chords = [[37,59,61,71,80],[38,60,62,72,81],[39,61,63,73,82]];
function playChords() {
for (var i = 0; i < 9; i++){
playChord(i/2, chords[i%chords.length]);
}
}
function playChord(delay, chord) {
MIDI.chordOn(0, chord, 127, delay);
MIDI.chordOff(0, chord, delay+1);
}

how to add parameter to the function in angular interval

I'm writing a function which will iterate through an array with some time interval.
eg:arr=[1,2,3,4,5,6,7]
And I want to show each element with angular interval.
$interval(step,5000,7);
function step(i){
console.log(arr[i]);
}
So it'll output the numbers inside the array in a 5 seconds' interval. How can I pass the parameter into the function? Thanks.
You could also do this way:
var arrOrig =[1,2,3,4,5,6,7],
arr = angular.copy(arrOrig), //if you want it for later
cancelPromise = $interval(step, 5000);
function step() {
console.log(arr.shift()); //take out the item from top of array
if (!arr.length) { //once array runs out
$interval.cancel(cancelPromise); //cancel interval
}
}
var arr = [1, 2, 3, 4, 5, 6, 7];
var $interval = angular.injector(['ng']).get('$interval');
var cancelPromise = $interval(step, 5000);
function step() {
console.log(arr.shift());
if (!arr.length) {
$interval.cancel(cancelPromise);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
arr.forEach(function (number, idx) {
$timeout(function () {
console.log(number);
}, 5000 * (idx + 1));
});
You want to use $timeout rather than $interval because calling $interval will set up an interval each time for each number and it won't stop until it's cleared explicitly.
var i=0;
$interval(function(){
step(i);
i++
},5000);
function step(i){
console.log(arr[i]);
}
This code will display the numbers one at a time, on repeat:
var getNextNumber = (function(){
var i = 0;
var numbers = [1,2,3,4,5,6,7];
return function(){
if(i >= numbers.length){
i = 0;
}
return numbers[i++];
};
})();
$interval(function(){
var number = getNextNumber();
console.log(number);
},5000);
See this working jsfiddle.
If you want to display them only once, use one of the $timeout solutions from one of the other answers. $interval is all about repeating some command for an indefinite amount of time, whereas $timeout is more about performing a command once in the future. See this answer for more.

Resources